Imported Upstream version 3.6.3 06/155906/1 upstream/3.6.3
authorDongHun Kwak <dh0128.kwak@samsung.com>
Mon, 16 Oct 2017 11:05:22 +0000 (20:05 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Mon, 16 Oct 2017 11:05:33 +0000 (20:05 +0900)
Change-Id: I517f0592d098f9860b48c8a5acf3d9d7a83fe3d0
Signed-off-by: DongHun Kwak <dh0128.kwak@samsung.com>
390 files changed:
Doc/Makefile
Doc/README.rst
Doc/bugs.rst
Doc/c-api/structures.rst
Doc/c-api/typeobj.rst
Doc/conf.py
Doc/copyright.rst
Doc/extending/extending.rst
Doc/extending/newtypes.rst
Doc/faq/general.rst
Doc/faq/gui.rst
Doc/faq/programming.rst
Doc/howto/curses.rst
Doc/howto/descriptor.rst
Doc/howto/logging-cookbook.rst
Doc/includes/noddy4.c
Doc/installing/index.rst
Doc/library/abc.rst
Doc/library/aifc.rst
Doc/library/argparse.rst
Doc/library/asyncio-dev.rst
Doc/library/asyncio-eventloop.rst
Doc/library/asyncio-eventloops.rst
Doc/library/asyncio-protocol.rst
Doc/library/asyncio-queue.rst
Doc/library/asyncio-stream.rst
Doc/library/asyncio-subprocess.rst
Doc/library/asyncio-sync.rst
Doc/library/asyncio-task.rst
Doc/library/codecs.rst
Doc/library/functions.rst
Doc/library/gettext.rst
Doc/library/hashlib.rst
Doc/library/idle.rst
Doc/library/importlib.rst
Doc/library/json.rst
Doc/library/multiprocessing.rst
Doc/library/re.rst
Doc/library/ssl.rst
Doc/library/stdtypes.rst
Doc/library/subprocess.rst
Doc/library/threading.rst
Doc/library/tokenize.rst
Doc/library/turtle.rst
Doc/library/typing.rst
Doc/make.bat
Doc/reference/datamodel.rst
Doc/reference/import.rst
Doc/reference/lexical_analysis.rst
Doc/tools/extensions/pyspecific.py
Doc/tools/static/switchers.js [new file with mode: 0644]
Doc/tools/static/version_switch.js [deleted file]
Doc/tools/susp-ignored.csv
Doc/tools/templates/customsourcelink.html
Doc/tools/templates/download.html
Doc/tools/templates/layout.html
Doc/tutorial/classes.rst
Doc/tutorial/controlflow.rst
Doc/using/unix.rst
Doc/using/windows.rst
Doc/whatsnew/2.6.rst
Doc/whatsnew/3.4.rst
Doc/whatsnew/3.6.rst
Doc/whatsnew/changelog.rst
Doc/whatsnew/index.rst
Grammar/Grammar
Include/ceval.h
Include/fileobject.h
Include/patchlevel.h
Include/pymath.h
Include/unicodeobject.h
LICENSE
Lib/asyncio/coroutines.py
Lib/asyncio/events.py
Lib/asyncio/test_utils.py
Lib/asyncore.py
Lib/collections/__init__.py
Lib/concurrent/futures/_base.py
Lib/concurrent/futures/process.py
Lib/concurrent/futures/thread.py
Lib/copyreg.py
Lib/ctypes/test/test_loading.py
Lib/ctypes/test/test_pep3118.py
Lib/ctypes/test/test_slicing.py
Lib/datetime.py
Lib/distutils/_msvccompiler.py
Lib/distutils/command/bdist_wininst.py
Lib/distutils/tests/test_msvccompiler.py
Lib/email/_header_value_parser.py
Lib/enum.py
Lib/ftplib.py
Lib/gettext.py
Lib/idlelib/ChangeLog
Lib/idlelib/NEWS.txt
Lib/idlelib/README.txt
Lib/idlelib/autocomplete.py
Lib/idlelib/autoexpand.py
Lib/idlelib/browser.py
Lib/idlelib/calltip_w.py
Lib/idlelib/calltips.py
Lib/idlelib/codecontext.py
Lib/idlelib/config-extensions.def
Lib/idlelib/config-keys.def
Lib/idlelib/config.py
Lib/idlelib/config_key.py
Lib/idlelib/configdialog.py
Lib/idlelib/editor.py
Lib/idlelib/grep.py
Lib/idlelib/help.html
Lib/idlelib/help_about.py
Lib/idlelib/idle_test/README.txt
Lib/idlelib/idle_test/htest.py
Lib/idlelib/idle_test/mock_idle.py
Lib/idlelib/idle_test/test_calltips.py
Lib/idlelib/idle_test/test_config.py
Lib/idlelib/idle_test/test_config_key.py
Lib/idlelib/idle_test/test_configdialog.py
Lib/idlelib/idle_test/test_help_about.py
Lib/idlelib/idle_test/test_outwin.py [new file with mode: 0644]
Lib/idlelib/idle_test/test_parenmatch.py
Lib/idlelib/idle_test/test_textview.py
Lib/idlelib/macosx.py
Lib/idlelib/mainmenu.py
Lib/idlelib/outwin.py
Lib/idlelib/paragraph.py
Lib/idlelib/parenmatch.py
Lib/idlelib/pyshell.py
Lib/idlelib/rstrip.py
Lib/idlelib/runscript.py
Lib/idlelib/scrolledlist.py
Lib/idlelib/textview.py
Lib/idlelib/zoomheight.py
Lib/idlelib/zzdummy.py [new file with mode: 0644]
Lib/imp.py
Lib/importlib/_bootstrap.py
Lib/json/__init__.py
Lib/logging/__init__.py
Lib/mimetypes.py
Lib/multiprocessing/process.py
Lib/multiprocessing/queues.py
Lib/os.py
Lib/pydoc_data/topics.py
Lib/socket.py
Lib/ssl.py
Lib/subprocess.py
Lib/test/_test_multiprocessing.py
Lib/test/bisect.py [new file with mode: 0755]
Lib/test/cfgparser.1
Lib/test/datetimetester.py
Lib/test/fork_wait.py
Lib/test/libregrtest/__init__.py
Lib/test/libregrtest/cmdline.py
Lib/test/libregrtest/main.py
Lib/test/libregrtest/refleak.py
Lib/test/libregrtest/runtest.py
Lib/test/lock_tests.py
Lib/test/mod_generics_cache.py
Lib/test/pythoninfo.py [new file with mode: 0644]
Lib/test/sha256.pem [deleted file]
Lib/test/support/__init__.py
Lib/test/test_asyncio/test_events.py
Lib/test/test_asyncio/test_futures.py
Lib/test/test_asyncio/test_proactor_events.py
Lib/test/test_asyncio/test_selector_events.py
Lib/test/test_asyncio/test_subprocess.py
Lib/test/test_asyncio/test_tasks.py
Lib/test/test_asyncio/test_windows_events.py
Lib/test/test_asyncore.py
Lib/test/test_audioop.py
Lib/test/test_builtin.py
Lib/test/test_code.py
Lib/test/test_concurrent_futures.py
Lib/test/test_copyreg.py
Lib/test/test_curses.py
Lib/test/test_datetime.py
Lib/test/test_decimal.py
Lib/test/test_email/test__header_value_parser.py
Lib/test/test_enum.py
Lib/test/test_exceptions.py
Lib/test/test_extcall.py
Lib/test/test_float.py
Lib/test/test_fstring.py
Lib/test/test_ftplib.py
Lib/test/test_getargs2.py
Lib/test/test_gettext.py
Lib/test/test_grp.py
Lib/test/test_hashlib.py
Lib/test/test_httpservers.py
Lib/test/test_imp.py
Lib/test/test_import/__init__.py
Lib/test/test_import/data/package/__init__.py [new file with mode: 0644]
Lib/test/test_import/data/package/submodule.py [new file with mode: 0644]
Lib/test/test_import/data/package2/submodule1.py [new file with mode: 0644]
Lib/test/test_import/data/package2/submodule2.py [new file with mode: 0644]
Lib/test/test_importlib/import_/test___package__.py
Lib/test/test_io.py
Lib/test/test_json/test_speedups.py
Lib/test/test_listcomps.py
Lib/test/test_locale.py
Lib/test/test_logging.py
Lib/test/test_normalization.py
Lib/test/test_os.py
Lib/test/test_poplib.py
Lib/test/test_posix.py
Lib/test/test_print.py
Lib/test/test_pydoc.py
Lib/test/test_queue.py
Lib/test/test_re.py
Lib/test/test_readline.py
Lib/test/test_regrtest.py
Lib/test/test_shutil.py
Lib/test/test_signal.py
Lib/test/test_smtplib.py
Lib/test/test_ssl.py
Lib/test/test_subprocess.py
Lib/test/test_tempfile.py
Lib/test/test_thread.py
Lib/test/test_threaded_import.py
Lib/test/test_threading.py
Lib/test/test_time.py
Lib/test/test_tokenize.py
Lib/test/test_traceback.py
Lib/test/test_typing.py
Lib/test/test_unicode.py
Lib/test/test_urllib2_localnet.py
Lib/test/test_urllib2net.py
Lib/test/test_warnings/__init__.py
Lib/test/test_winsound.py
Lib/test/test_xml_etree.py
Lib/test/test_xml_etree_c.py
Lib/test/test_xmlrpc.py
Lib/test/test_zipimport.py
Lib/tkinter/test/test_ttk/test_extensions.py
Lib/tkinter/ttk.py
Lib/typing.py
Lib/unittest/mock.py
Lib/unittest/test/test_discovery.py
Lib/unittest/test/testmock/testhelpers.py
Lib/xmlrpc/server.py
Mac/BuildScript/build-installer.py
Mac/README
Makefile.pre.in
Misc/ACKS
Misc/NEWS
Misc/Porting
Misc/python.man
Modules/Setup.dist
Modules/_asynciomodule.c
Modules/_collectionsmodule.c
Modules/_ctypes/_ctypes.c
Modules/_ctypes/callproc.c
Modules/_ctypes/cfield.c
Modules/_cursesmodule.c
Modules/_decimal/_decimal.c
Modules/_decimal/libmpdec/io.c
Modules/_elementtree.c
Modules/_functoolsmodule.c
Modules/_hashopenssl.c
Modules/_io/bytesio.c
Modules/_io/fileio.c
Modules/_io/textio.c
Modules/_io/winconsoleio.c
Modules/_json.c
Modules/_localemodule.c
Modules/_pickle.c
Modules/_ssl.c
Modules/_struct.c
Modules/_testcapimodule.c
Modules/_tkinter.c
Modules/_tracemalloc.c
Modules/_winapi.c
Modules/audioop.c
Modules/cjkcodecs/_codecs_iso2022.c
Modules/clinic/_asynciomodule.c.h
Modules/clinic/_ssl.c.h
Modules/expat/ascii.h
Modules/expat/asciitab.h
Modules/expat/expat.h
Modules/expat/expat_external.h
Modules/expat/iasciitab.h
Modules/expat/internal.h
Modules/expat/latin1tab.h
Modules/expat/loadlibrary.c [new file with mode: 0644]
Modules/expat/nametab.h
Modules/expat/siphash.h
Modules/expat/utf8tab.h
Modules/expat/winconfig.h
Modules/expat/xmlparse.c
Modules/expat/xmlrole.c
Modules/expat/xmlrole.h
Modules/expat/xmltok.c
Modules/expat/xmltok.h
Modules/expat/xmltok_impl.c
Modules/expat/xmltok_impl.h
Modules/expat/xmltok_ns.c
Modules/grpmodule.c
Modules/nismodule.c
Modules/overlapped.c
Modules/posixmodule.c
Modules/pwdmodule.c
Modules/readline.c
Modules/selectmodule.c
Modules/signalmodule.c
Modules/socketmodule.c
Modules/spwdmodule.c
Modules/timemodule.c
Modules/zipimport.c
Objects/abstract.c
Objects/codeobject.c
Objects/dictobject.c
Objects/exceptions.c
Objects/floatobject.c
Objects/odictobject.c
Objects/setobject.c
Objects/stringlib/codecs.h
Objects/stringlib/unicode_format.h
Objects/typeobject.c
Objects/unicodeobject.c
PC/_findvs.cpp [new file with mode: 0644]
PC/_msi.c
PC/config.c
PC/external/Externals.txt [new file with mode: 0644]
PC/external/include/Setup.Configuration.h [new file with mode: 0644]
PC/external/v140/amd64/Microsoft.VisualStudio.Setup.Configuration.Native.lib [new file with mode: 0644]
PC/external/v140/win32/Microsoft.VisualStudio.Setup.Configuration.Native.lib [new file with mode: 0644]
PC/external/v141/amd64/Microsoft.VisualStudio.Setup.Configuration.Native.lib [new file with mode: 0644]
PC/external/v141/win32/Microsoft.VisualStudio.Setup.Configuration.Native.lib [new file with mode: 0644]
PC/python.manifest
PC/winsound.c
PCbuild/_elementtree.vcxproj
PCbuild/_elementtree.vcxproj.filters
PCbuild/_lzma.vcxproj
PCbuild/build.bat
PCbuild/find_msbuild.bat
PCbuild/find_python.bat [new file with mode: 0644]
PCbuild/get_externals.bat
PCbuild/pyexpat.vcxproj
PCbuild/pyexpat.vcxproj.filters
PCbuild/pyproject.props
PCbuild/python.props
PCbuild/python.vcxproj
PCbuild/pythoncore.vcxproj
PCbuild/tcl.vcxproj
PCbuild/tcltk.props
PCbuild/tk.vcxproj
PCbuild/urlretrieve.py [new file with mode: 0644]
Parser/asdl_c.py
Python/Python-ast.c
Python/_warnings.c
Python/ast.c
Python/bltinmodule.c
Python/ceval.c
Python/clinic/bltinmodule.c.h
Python/compile.c
Python/dtoa.c
Python/dynload_win.c
Python/errors.c
Python/fileutils.c
Python/formatter_unicode.c
Python/getargs.c
Python/import.c
Python/importlib.h
Python/marshal.c
Python/pyhash.c
Python/pytime.c
Python/thread_pthread.h
Python/wordcode_helpers.h
README.rst
Tools/gdb/libpython.py
Tools/msi/buildrelease.bat
Tools/msi/exe/exe.wixproj
Tools/msi/exe/exe_files.wxs
Tools/msi/get_externals.bat
Tools/msi/make_zip.proj
Tools/msi/make_zip.py
Tools/msi/uploadrelease.bat
Tools/nuget/build.bat
Tools/nuget/make_pkg.proj
Tools/nuget/python.nuspec
Tools/nuget/python.props [new file with mode: 0644]
Tools/nuget/pythondaily.nuspec [new file with mode: 0644]
Tools/nuget/pythonx86.nuspec
Tools/scripts/patchcheck.py
Tools/ssl/multissltests.py [new file with mode: 0755]
Tools/ssl/test_multiple_versions.py [deleted file]
aclocal.m4
configure
configure.ac
pyconfig.h.in
setup.py

index ae59f32..307d1e0 100644 (file)
@@ -5,7 +5,9 @@
 
 # You can set these variables from the command line.
 PYTHON       = python3
-SPHINXBUILD  = sphinx-build
+VENVDIR      = ./venv
+SPHINXBUILD  = PATH=$(VENVDIR)/bin:$$PATH sphinx-build
+BLURB        = PATH=$(VENVDIR)/bin:$$PATH blurb
 PAPER        =
 SOURCES      =
 DISTVERSION  = $(shell $(PYTHON) tools/extensions/patchlevel.py)
@@ -38,6 +40,20 @@ help:
        @echo "  serve      to serve the documentation on the localhost (8000)"
 
 build:
+       -mkdir -p build
+# Look first for a Misc/NEWS file (building from a source release tarball
+# or old repo) and use that, otherwise look for a Misc/NEWS.d directory
+# (building from a newer repo) and use blurb to generate the NEWS file.
+       @if [ -f  ../Misc/NEWS ] ; then \
+               echo "Using existing Misc/NEWS file"; \
+               cp ../Misc/NEWS build/NEWS; \
+       elif [ -d ../Misc/NEWS.d ]; then \
+               echo "Building NEWS from Misc/NEWS.d with blurb"; \
+               $(BLURB) merge -f build/NEWS; \
+       else \
+               echo "Neither Misc/NEWS.d nor Misc/NEWS found; cannot build docs"; \
+               exit 1; \
+       fi
        $(SPHINXBUILD) $(ALLSPHINXOPTS)
        @echo
 
@@ -103,11 +119,12 @@ htmlview: html
         $(PYTHON) -c "import webbrowser; webbrowser.open('build/html/index.html')"
 
 clean:
-       -rm -rf build/* venv/*
+       -rm -rf build/* $(VENVDIR)/*
 
 venv:
-       $(PYTHON) -m venv venv
-       ./venv/bin/python3 -m pip install -U Sphinx
+       $(PYTHON) -m venv $(VENVDIR)
+       $(VENVDIR)/bin/python3 -m pip install -U Sphinx blurb
+       @echo "The venv has been created in the $(VENVDIR) directory"
 
 dist:
        rm -rf dist
@@ -153,7 +170,7 @@ dist:
        cp -pPR build/epub/Python.epub dist/python-$(DISTVERSION)-docs.epub
 
 check:
-       $(PYTHON) tools/rstlint.py -i tools -i venv -i README.rst
+       $(PYTHON) tools/rstlint.py -i tools -i $(VENVDIR) -i README.rst
 
 serve:
        ../Tools/scripts/serve.py build/html
@@ -167,12 +184,12 @@ serve:
 
 # for development releases: always build
 autobuild-dev:
-       make dist SPHINXOPTS='$(SPHINXOPTS) -Ea -A daily=1 -A versionswitcher=1'
+       make dist SPHINXOPTS='$(SPHINXOPTS) -Ea -A daily=1 -A switchers=1'
        -make suspicious
 
 # for quick rebuilds (HTML only)
 autobuild-dev-html:
-       make html SPHINXOPTS='$(SPHINXOPTS) -Ea -A daily=1 -A versionswitcher=1'
+       make html SPHINXOPTS='$(SPHINXOPTS) -Ea -A daily=1 -A switchers=1'
 
 # for stable releases: only build if not in pre-release stage (alpha, beta)
 # release candidate downloads are okay, since the stable tree can be in that stage
index dcd3d6e..a29d1f3 100644 (file)
@@ -7,45 +7,53 @@ available <https://docs.python.org/dev/download.html>`_.
 
 Documentation on authoring Python documentation, including information about
 both style and markup, is available in the "`Documenting Python
-<https://docs.python.org/devguide/documenting.html>`_" chapter of the
+<https://devguide.python.org/documenting/>`_" chapter of the
 developers guide.
 
 
 Building the docs
 =================
 
-You need to have `Sphinx <http://sphinx-doc.org/>`_ installed; it is the toolset
-used to build the docs.  It is not included in this tree, but maintained
-separately and `available from PyPI <https://pypi.python.org/pypi/Sphinx>`_.
+The documentation is built with several tools which are not included in this
+tree but are maintained separately and are available from
+`PyPI <https://pypi.org/>`_.
+
+* `Sphinx <https://pypi.org/project/Sphinx/>`_
+* `blurb <https://pypi.org/project/blurb/>`_
+
+The easiest way to install these tools is to create a virtual environment and
+install the tools into there.
 
 
 Using make
 ----------
 
-A Makefile has been prepared so that on Unix, provided you have installed
-Sphinx, you can just run ::
+To get started on UNIX, you can create a virtual environment with the command ::
 
-   make html
+  make venv
 
-to build the HTML output files.
-
-On Windows, we try to emulate the Makefile as closely as possible with a
-``make.bat`` file.
+That will install all the tools necessary to build the documentation. Assuming
+the virtual environment was created in the ``env`` directory (the default;
+configurable with the VENVDIR variable), you can run the following command to
+build the HTML output files::
 
-To use a Python interpreter that's not called ``python``, use the standard
-way to set Makefile variables, using e.g. ::
+  make html
 
-   make html PYTHON=python3
+By default, if the virtual environment is not created, the Makefile will
+look for instances of sphinxbuild and blurb installed on your process PATH
+(configurable with the SPHINXBUILD and BLURB variables).
 
-On Windows, set the PYTHON environment variable instead.
-
-To use a specific sphinx-build (something other than ``sphinx-build``), set
-the SPHINXBUILD variable.
+On Windows, we try to emulate the Makefile as closely as possible with a
+``make.bat`` file. If you need to specify the Python interpreter to use,
+set the PYTHON environment variable instead.
 
 Available make targets are:
 
 * "clean", which removes all build files.
 
+* "venv", which creates a virtual environment with all necessary tools
+  installed.
+
 * "html", which builds standalone HTML files for offline viewing.
 
 * "htmlview", which re-uses the "html" builder, but then opens the main page
@@ -96,7 +104,7 @@ Available make targets are:
 Without make
 ------------
 
-Install the Sphinx package and its dependencies from PyPI.
+First, install the tool dependencies from PyPI.
 
 Then, from the ``Doc`` directory, run ::
 
@@ -109,11 +117,10 @@ see the make targets above).
 Contributing
 ============
 
-Bugs in the content should be reported to the 
+Bugs in the content should be reported to the
 `Python bug tracker <https://bugs.python.org>`_.
 
-Bugs in the toolset should be reported in the 
-`Sphinx bug tracker <https://github.com/sphinx-doc/sphinx/issues>`_.
+Bugs in the toolset should be reported to the tools themselves.
 
 You can also send a mail to the Python Documentation Team at docs@python.org,
 and we will process your request as soon as possible.
index 1b0a5a9..bc1d10f 100644 (file)
@@ -88,5 +88,5 @@ the `core-mentorship mailing list`_ is a friendly place to get answers to
 any and all questions pertaining to the process of fixing issues in Python.
 
 .. _Documentation bugs: https://bugs.python.org/issue?@filter=status&@filter=components&components=4&status=1&@columns=id,activity,title,status&@sort=-activity
-.. _Python Developer's Guide: https://docs.python.org/devguide/
+.. _Python Developer's Guide: https://devguide.python.org/
 .. _core-mentorship mailing list: https://mail.python.org/mailman/listinfo/core-mentorship/
index c080f31..675f6f2 100644 (file)
@@ -294,3 +294,43 @@ definition with the same method name.
    read-only access.  Using :c:macro:`T_STRING` for :attr:`type` implies
    :c:macro:`READONLY`.  Only :c:macro:`T_OBJECT` and :c:macro:`T_OBJECT_EX`
    members can be deleted.  (They are set to *NULL*).
+
+
+.. c:type:: PyGetSetDef
+
+   Structure to define property-like access for a type. See also description of
+   the :c:member:`PyTypeObject.tp_getset` slot.
+
+   +-------------+------------------+-----------------------------------+
+   | Field       | C Type           | Meaning                           |
+   +=============+==================+===================================+
+   | name        | char \*          | attribute name                    |
+   +-------------+------------------+-----------------------------------+
+   | get         | getter           | C Function to get the attribute   |
+   +-------------+------------------+-----------------------------------+
+   | set         | setter           | optional C function to set or     |
+   |             |                  | delete the attribute, if omitted  |
+   |             |                  | the attribute is readonly         |
+   +-------------+------------------+-----------------------------------+
+   | doc         | char \*          | optional docstring                |
+   +-------------+------------------+-----------------------------------+
+   | closure     | void \*          | optional function pointer,        |
+   |             |                  | providing additional data for     |
+   |             |                  | getter and setter                 |
+   +-------------+------------------+-----------------------------------+
+
+   The ``get`` function takes one :c:type:`PyObject\*` parameter (the
+   instance) and a function pointer (the associated ``closure``)::
+
+      typedef PyObject *(*getter)(PyObject *, void *);
+
+   It should return a new reference on success or *NULL* with a set exception
+   on failure.
+
+   ``set`` functions take two :c:type:`PyObject\*` parameters (the instance and
+   the value to be set) and a function pointer (the associated ``closure``)::
+
+      typedef int (*setter)(PyObject *, PyObject *, void *);
+
+   In case the attribute should be deleted the second parameter is *NULL*.
+   Should return ``0`` on success or ``-1`` with a set exception on failure.
index ac6fd0b..0b4577f 100644 (file)
@@ -719,21 +719,6 @@ type objects) *must* have the :attr:`ob_size` field.
    This field is not inherited by subtypes (computed attributes are inherited
    through a different mechanism).
 
-   .. XXX belongs elsewhere
-
-   Docs for PyGetSetDef::
-
-      typedef PyObject *(*getter)(PyObject *, void *);
-      typedef int (*setter)(PyObject *, PyObject *, void *);
-
-      typedef struct PyGetSetDef {
-          char *name;    /* attribute name */
-          getter get;    /* C function to get the attribute */
-          setter set;    /* C function to set or delete the attribute */
-          char *doc;     /* optional doc string */
-          void *closure; /* optional additional data for getter and setter */
-      } PyGetSetDef;
-
 
 .. c:member:: PyTypeObject* PyTypeObject.tp_base
 
index 18aebb6..d4ee50d 100644 (file)
@@ -89,13 +89,17 @@ html_split_index = True
 # ------------------------
 
 # Get LaTeX to handle Unicode correctly
-latex_elements = {'inputenc': r'\usepackage[utf8x]{inputenc}', 'utf8extra': ''}
+latex_elements = {
+    'inputenc': r'\usepackage[utf8x]{inputenc}',
+    'utf8extra': '',
+    'fontenc': r'\usepackage[T1,T2A]{fontenc}',
+}
 
 # Additional stuff for the LaTeX preamble.
 latex_elements['preamble'] = r'''
 \authoraddress{
-  \strong{Python Software Foundation}\\
-  Email: \email{docs@python.org}
+  \sphinxstrong{Python Software Foundation}\\
+  Email: \sphinxemail{docs@python.org}
 }
 \let\Verbatim=\OriginalVerbatim
 \let\endVerbatim=\endOriginalVerbatim
@@ -105,7 +109,7 @@ latex_elements['preamble'] = r'''
 latex_elements['papersize'] = 'a4'
 
 # The font size ('10pt', '11pt' or '12pt').
-latex_elements['font_size'] = '10pt'
+latex_elements['pointsize'] = '10pt'
 
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title, author, document class [howto/manual]).
index 22d7705..2b5400c 100644 (file)
@@ -4,7 +4,7 @@ Copyright
 
 Python and this documentation is:
 
-Copyright © 2001-2016 Python Software Foundation. All rights reserved.
+Copyright © 2001-2017 Python Software Foundation. All rights reserved.
 
 Copyright © 2000 BeOpen.com. All rights reserved.
 
index 4ac820a..9358462 100644 (file)
@@ -333,12 +333,12 @@ function.
 The method table must be referenced in the module definition structure::
 
    static struct PyModuleDef spammodule = {
-      PyModuleDef_HEAD_INIT,
-      "spam",   /* name of module */
-      spam_doc, /* module documentation, may be NULL */
-      -1,       /* size of per-interpreter state of the module,
-                   or -1 if the module keeps state in global variables. */
-      SpamMethods
+       PyModuleDef_HEAD_INIT,
+       "spam",   /* name of module */
+       spam_doc, /* module documentation, may be NULL */
+       -1,       /* size of per-interpreter state of the module,
+                    or -1 if the module keeps state in global variables. */
+       SpamMethods
    };
 
 This structure, in turn, must be passed to the interpreter in the module's
index 003b4e5..abd5da9 100644 (file)
@@ -728,8 +728,9 @@ functions.  With :c:func:`Py_VISIT`, :c:func:`Noddy_traverse` can be simplified:
    uniformity across these boring implementations.
 
 We also need to provide a method for clearing any subobjects that can
-participate in cycles.  We implement the method and reimplement the deallocator
-to use it::
+participate in cycles.
+
+::
 
    static int
    Noddy_clear(Noddy *self)
@@ -747,13 +748,6 @@ to use it::
        return 0;
    }
 
-   static void
-   Noddy_dealloc(Noddy* self)
-   {
-       Noddy_clear(self);
-       Py_TYPE(self)->tp_free((PyObject*)self);
-   }
-
 Notice the use of a temporary variable in :c:func:`Noddy_clear`. We use the
 temporary variable so that we can set each member to *NULL* before decrementing
 its reference count.  We do this because, as was discussed earlier, if the
@@ -776,6 +770,23 @@ be simplified::
        return 0;
    }
 
+Note that :c:func:`Noddy_dealloc` may call arbitrary functions through
+``__del__`` method or weakref callback. It means circular GC can be
+triggered inside the function.  Since GC assumes reference count is not zero,
+we need to untrack the object from GC by calling :c:func:`PyObject_GC_UnTrack`
+before clearing members. Here is reimplemented deallocator which uses
+:c:func:`PyObject_GC_UnTrack` and :c:func:`Noddy_clear`.
+
+::
+
+   static void
+   Noddy_dealloc(Noddy* self)
+   {
+       PyObject_GC_UnTrack(self);
+       Noddy_clear(self);
+       Py_TYPE(self)->tp_free((PyObject*)self);
+   }
+
 Finally, we add the :const:`Py_TPFLAGS_HAVE_GC` flag to the class flags::
 
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */
index 8f6a907..d4a97fd 100644 (file)
@@ -167,7 +167,7 @@ several useful pieces of freely distributable software.  The source will compile
 and run out of the box on most UNIX platforms.
 
 Consult the `Getting Started section of the Python Developer's Guide
-<https://docs.python.org/devguide/setup.html>`__ for more
+<https://devguide.python.org/setup/>`__ for more
 information on getting the source code and compiling it.
 
 
@@ -223,7 +223,7 @@ newsgroups and on the Python home page at https://www.python.org/; an RSS feed o
 news is available.
 
 You can also access the development version of Python through Git.  See
-`The Python Developer's Guide <https://docs.python.org/devguide/>`_ for details.
+`The Python Developer's Guide <https://devguide.python.org/>`_ for details.
 
 
 How do I submit bug reports and patches for Python?
@@ -239,7 +239,7 @@ report bugs to Python, you can obtain your Roundup password through Roundup's
 `password reset procedure <https://bugs.python.org/user?@template=forgotten>`_.
 
 For more information on how Python is developed, consult `the Python Developer's
-Guide <https://docs.python.org/devguide/>`_.
+Guide <https://devguide.python.org/>`_.
 
 
 Are there any published articles about Python that I can reference?
index 477d8c6..38e1796 100644 (file)
@@ -94,15 +94,6 @@ Python bindings for `the FLTK toolkit <http://www.fltk.org>`_, a simple yet
 powerful and mature cross-platform windowing system, are available from `the
 PyFLTK project <http://pyfltk.sourceforge.net>`_.
 
-
-FOX
-----
-
-A wrapper for `the FOX toolkit <http://www.fox-toolkit.org/>`_ called `FXpy
-<http://fxpy.sourceforge.net/>`_ is available.  FOX supports both Unix variants
-and Windows.
-
-
 OpenGL
 ------
 
index 9c5e20d..7476ce1 100644 (file)
@@ -1638,7 +1638,7 @@ collected.
 
 Despite the cycle collector, it's still a good idea to define an explicit
 ``close()`` method on objects to be called whenever you're done with them.  The
-``close()`` method can then remove attributes that refer to subobjecs.  Don't
+``close()`` method can then remove attributes that refer to subobjects.  Don't
 call :meth:`__del__` directly -- :meth:`__del__` should call ``close()`` and
 ``close()`` should make sure that it can be called more than once for the same
 object.
index 188a5cf..1d3bfb8 100644 (file)
@@ -65,7 +65,7 @@ and full support for mouse and keyboard input.
 The Python curses module
 ------------------------
 
-Thy Python module is a fairly simple wrapper over the C functions provided by
+The Python module is a fairly simple wrapper over the C functions provided by
 curses; if you're already familiar with curses programming in C, it's really
 easy to transfer that knowledge to Python.  The biggest difference is that the
 Python interface makes things simpler by merging different C functions such as
@@ -538,7 +538,7 @@ the Python interface.  Often this isn't because they're difficult to
 implement, but because no one has needed them yet.  Also, Python
 doesn't yet support the menu library associated with ncurses.
 Patches adding support for these would be welcome; see
-`the Python Developer's Guide <https://docs.python.org/devguide/>`_ to
+`the Python Developer's Guide <https://devguide.python.org/>`_ to
 learn more about submitting patches to Python.
 
 * `Writing Programs with NCURSES <http://invisible-island.net/ncurses/ncurses-intro.html>`_:
index 2dd6c34..b349375 100644 (file)
@@ -252,10 +252,10 @@ to wrap access to the value attribute in a property data descriptor::
 
     class Cell(object):
         . . .
-        def getvalue(self, obj):
-            "Recalculate cell before returning value"
+        def getvalue(self):
+            "Recalculate the cell before returning value"
             self.recalc()
-            return obj._value
+            return self._value
         value = property(getvalue)
 
 
index 6498ea5..44f38e0 100644 (file)
@@ -1258,8 +1258,8 @@ socket is created separately and passed to the handler (as its 'queue')::
 
     class ZeroMQSocketHandler(QueueHandler):
         def enqueue(self, record):
-            data = json.dumps(record.__dict__)
-            self.queue.send(data)
+            self.queue.send_json(record.__dict__)
+
 
     handler = ZeroMQSocketHandler(sock)
 
@@ -1272,11 +1272,10 @@ data needed by the handler to create the socket::
             self.ctx = ctx or zmq.Context()
             socket = zmq.Socket(self.ctx, socktype)
             socket.bind(uri)
-            QueueHandler.__init__(self, socket)
+            super().__init__(socket)
 
         def enqueue(self, record):
-            data = json.dumps(record.__dict__)
-            self.queue.send(data)
+            self.queue.send_json(record.__dict__)
 
         def close(self):
             self.queue.close()
@@ -1292,12 +1291,13 @@ of queues, for example a ZeroMQ 'subscribe' socket. Here's an example::
         def __init__(self, uri, *handlers, **kwargs):
             self.ctx = kwargs.get('ctx') or zmq.Context()
             socket = zmq.Socket(self.ctx, zmq.SUB)
-            socket.setsockopt(zmq.SUBSCRIBE, '')  # subscribe to everything
+            socket.setsockopt_string(zmq.SUBSCRIBE, '')  # subscribe to everything
             socket.connect(uri)
+            super().__init__(socket, *handlers, **kwargs)
 
         def dequeue(self):
-            msg = self.queue.recv()
-            return logging.makeLogRecord(json.loads(msg))
+            msg = self.queue.recv_json()
+            return logging.makeLogRecord(msg)
 
 
 .. seealso::
index eb9622a..08ba4c3 100644 (file)
@@ -46,6 +46,7 @@ Noddy_clear(Noddy *self)
 static void
 Noddy_dealloc(Noddy* self)
 {
+    PyObject_GC_UnTrack(self);
     Noddy_clear(self);
     Py_TYPE(self)->tp_free((PyObject*)self);
 }
index b22465d..09bb825 100644 (file)
@@ -211,6 +211,17 @@ On such systems, it is often better to use a virtual environment or a
 per-user installation when installing packages with ``pip``.
 
 
+Pip not installed
+-----------------
+
+It is possible that ``pip`` does not get installed by default. One potential fix is::
+
+    python -m ensurepip --default-pip
+
+There are also additional resources for `installing pip.
+<https://packaging.python.org/tutorials/installing-packages/#install-pip-setuptools-and-wheel>`__
+
+
 Installing binary extensions
 ----------------------------
 
index 966003b..6001db3 100644 (file)
@@ -24,7 +24,33 @@ a class or instance provides a particular interface, for example, is it
 hashable or a mapping.
 
 
-This module provides the following classes:
+This module provides the metaclass :class:`ABCMeta` for defining ABCs and
+a helper class :class:`ABC` to alternatively define ABCs through inheritance:
+
+.. class:: ABC
+
+   A helper class that has :class:`ABCMeta` as its metaclass.  With this class,
+   an abstract base class can be created by simply deriving from :class:`ABC`
+   avoiding sometimes confusing metaclass usage, for example::
+
+     from abc import ABC
+
+     class MyABC(ABC):
+         pass
+
+   Note that the type of :class:`ABC` is still :class:`ABCMeta`, therefore
+   inheriting from :class:`ABC` requires the usual precautions regarding
+   metaclass usage, as multiple inheritance may lead to metaclass conflicts.
+   One may also define an abstract base class by passing the metaclass
+   keyword and using :class:`ABCMeta` directly, for example::
+
+     from abc import ABCMeta
+
+     class MyABC(metaclass=ABCMeta):
+         pass
+
+   .. versionadded:: 3.4
+
 
 .. class:: ABCMeta
 
@@ -46,15 +72,15 @@ This module provides the following classes:
       Register *subclass* as a "virtual subclass" of this ABC. For
       example::
 
-        from abc import ABCMeta
+         from abc import ABC
 
-        class MyABC(metaclass=ABCMeta):
-            pass
+         class MyABC(ABC):
+             pass
 
-        MyABC.register(tuple)
+         MyABC.register(tuple)
 
-        assert issubclass(tuple, MyABC)
-        assert isinstance((), MyABC)
+         assert issubclass(tuple, MyABC)
+         assert isinstance((), MyABC)
 
       .. versionchanged:: 3.3
          Returns the registered subclass, to allow usage as a class decorator.
@@ -95,7 +121,7 @@ This module provides the following classes:
           def get_iterator(self):
               return iter(self)
 
-      class MyIterable(metaclass=ABCMeta):
+      class MyIterable(ABC):
 
           @abstractmethod
           def __iter__(self):
@@ -132,17 +158,6 @@ This module provides the following classes:
    available as a method of ``Foo``, so it is provided separately.
 
 
-.. class:: ABC
-
-   A helper class that has :class:`ABCMeta` as its metaclass.  With this class,
-   an abstract base class can be created by simply deriving from :class:`ABC`,
-   avoiding sometimes confusing metaclass usage.
-
-   Note that the type of :class:`ABC` is still :class:`ABCMeta`, therefore
-   inheriting from :class:`ABC` requires the usual precautions regarding metaclass
-   usage, as multiple inheritance may lead to metaclass conflicts.
-
-   .. versionadded:: 3.4
 
 
 The :mod:`abc` module also provides the following decorators:
@@ -168,7 +183,7 @@ The :mod:`abc` module also provides the following decorators:
    descriptors, it should be applied as the innermost decorator, as shown in
    the following usage examples::
 
-      class C(metaclass=ABCMeta):
+      class C(ABC):
           @abstractmethod
           def my_abstract_method(self, ...):
               ...
@@ -230,7 +245,7 @@ The :mod:`abc` module also provides the following decorators:
    is now correctly identified as abstract when applied to an abstract
    method::
 
-      class C(metaclass=ABCMeta):
+      class C(ABC):
           @classmethod
           @abstractmethod
           def my_abstract_classmethod(cls, ...):
@@ -251,7 +266,7 @@ The :mod:`abc` module also provides the following decorators:
    is now correctly identified as abstract when applied to an abstract
    method::
 
-      class C(metaclass=ABCMeta):
+      class C(ABC):
           @staticmethod
           @abstractmethod
           def my_abstract_staticmethod(...):
@@ -278,7 +293,7 @@ The :mod:`abc` module also provides the following decorators:
    is now correctly identified as abstract when applied to an abstract
    method::
 
-      class C(metaclass=ABCMeta):
+      class C(ABC):
           @property
           @abstractmethod
           def my_abstract_property(self):
@@ -288,7 +303,7 @@ The :mod:`abc` module also provides the following decorators:
    read-write abstract property by appropriately marking one or more of the
    underlying methods as abstract::
 
-      class C(metaclass=ABCMeta):
+      class C(ABC):
           @property
           def x(self):
               ...
index 23a9620..970a7ae 100644 (file)
@@ -18,12 +18,6 @@ AIFF is Audio Interchange File Format, a format for storing digital audio
 samples in a file.  AIFF-C is a newer version of the format that includes the
 ability to compress the audio data.
 
-.. note::
-
-   Some operations may only work under IRIX; these will raise :exc:`ImportError`
-   when attempting to import the :mod:`cl` module, which is only available on
-   IRIX.
-
 Audio files have a number of parameters that describe the audio data. The
 sampling rate or frame rate is the number of times per second the sound is
 sampled.  The number of channels indicate if the audio is mono, stereo, or
index 4530304..27c7ba5 100644 (file)
@@ -426,7 +426,9 @@ should not be line-wrapped::
     -h, --help  show this help message and exit
 
 :class:`RawTextHelpFormatter` maintains whitespace for all sorts of help text,
-including argument descriptions.
+including argument descriptions. However, multiple new lines are replaced with
+one. If you wish to preserve multiple blank lines, add spaces between the
+newlines.
 
 :class:`ArgumentDefaultsHelpFormatter` automatically adds information about
 default values to each of the argument help messages::
@@ -898,6 +900,8 @@ values are:
      usage: PROG [-h] foo [foo ...]
      PROG: error: too few arguments
 
+.. _`argparse.REMAINDER`:
+
 * ``argparse.REMAINDER``.  All the remaining command-line arguments are gathered
   into a list.  This is commonly useful for command line utilities that dispatch
   to other command line utilities::
@@ -1324,8 +1328,11 @@ The parse_args() method
    created and how they are assigned. See the documentation for
    :meth:`add_argument` for details.
 
-   By default, the argument strings are taken from :data:`sys.argv`, and a new empty
-   :class:`Namespace` object is created for the attributes.
+   * args_ - List of strings to parse.  The default is taken from
+     :data:`sys.argv`.
+
+   * namespace_ - An object to take the attributes.  The default is a new empty
+     :class:`Namespace` object.
 
 
 Option value syntax
@@ -1467,6 +1474,7 @@ unambiguous (the prefix matches a unique option)::
 An error is produced for arguments that could produce more than one options.
 This feature can be disabled by setting :ref:`allow_abbrev` to ``False``.
 
+.. _args:
 
 Beyond ``sys.argv``
 ^^^^^^^^^^^^^^^^^^^
@@ -1488,6 +1496,7 @@ interactive prompt::
    >>> parser.parse_args(['1', '2', '3', '4', '--sum'])
    Namespace(accumulate=<built-in function sum>, integers=[1, 2, 3, 4])
 
+.. _namespace:
 
 The Namespace object
 ^^^^^^^^^^^^^^^^^^^^
@@ -2008,7 +2017,12 @@ A partial upgrade path from :mod:`optparse` to :mod:`argparse`:
 * Replace ``(options, args) = parser.parse_args()`` with ``args =
   parser.parse_args()`` and add additional :meth:`ArgumentParser.add_argument`
   calls for the positional arguments. Keep in mind that what was previously
-  called ``options``, now in :mod:`argparse` context is called ``args``.
+  called ``options``, now in the :mod:`argparse` context is called ``args``.
+
+* Replace :meth:`optparse.OptionParser.disable_interspersed_args`
+  by setting ``nargs`` of a positional argument to `argparse.REMAINDER`_, or
+  use :meth:`~ArgumentParser.parse_known_args` to collect unparsed argument
+  strings in a separate list.
 
 * Replace callback actions and the ``callback_*`` keyword arguments with
   ``type`` or ``action`` arguments.
index cc4a14b..1838eb9 100644 (file)
@@ -52,7 +52,7 @@ Cancellation
 ------------
 
 Cancellation of tasks is not common in classic programming. In asynchronous
-programming, not only it is something common, but you have to prepare your
+programming, not only is it something common, but you have to prepare your
 code to handle it.
 
 Futures and tasks can be cancelled explicitly with their :meth:`Future.cancel`
index fa6a296..83bbb70 100644 (file)
@@ -5,6 +5,8 @@
 Base Event Loop
 ===============
 
+**Source code:** :source:`Lib/asyncio/events.py`
+
 The event loop is the central execution device provided by :mod:`asyncio`.
 It provides multiple facilities, including:
 
index 1dc18fc..d74fcb1 100644 (file)
@@ -3,6 +3,8 @@
 Event loops
 ===========
 
+**Source code:** :source:`Lib/asyncio/events.py`
+
 Event loop functions
 --------------------
 
index 3f55506..cd84ae7 100644 (file)
@@ -1,8 +1,12 @@
 .. currentmodule:: asyncio
 
-++++++++++++++++++++++++++++++++++++++++++++++
-Transports  and protocols (callback based API)
-++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++
+Transports and protocols (callback based API)
++++++++++++++++++++++++++++++++++++++++++++++
+
+**Source code:** :source:`Lib/asyncio/transports.py`
+
+**Source code:** :source:`Lib/asyncio/protocols.py`
 
 .. _asyncio-transport:
 
@@ -163,11 +167,17 @@ WriteTransport
 
       Set the *high*- and *low*-water limits for write flow control.
 
-      These two values control when call the protocol's
+      These two values (measured in number of
+      bytes) control when the protocol's
       :meth:`pause_writing` and :meth:`resume_writing` methods are called.
       If specified, the low-water limit must be less than or equal to the
       high-water limit.  Neither *high* nor *low* can be negative.
 
+      :meth:`pause_writing` is called when the buffer size becomes greater
+      than or equal to the *high* value. If writing has been paused,
+      :meth:`resume_writing` is called when the buffer size becomes less
+      than or equal to the *low* value.
+
       The defaults are implementation-specific.  If only the
       high-water limit is given, the low-water limit defaults to an
       implementation-specific value less than or equal to the
index f11c09a..ea78755 100644 (file)
@@ -3,6 +3,8 @@
 Queues
 ======
 
+**Source code:** :source:`Lib/asyncio/queues.py`
+
 Queues:
 
 * :class:`Queue`
index 6177b4b..491afdd 100644 (file)
@@ -6,6 +6,8 @@
 Streams (coroutine based API)
 +++++++++++++++++++++++++++++
 
+**Source code:** :source:`Lib/asyncio/streams.py`
+
 Stream functions
 ================
 
index 16ba9a3..1c1d0be 100644 (file)
@@ -5,6 +5,8 @@
 Subprocess
 ==========
 
+**Source code:** :source:`Lib/asyncio/subprocess.py`
+
 Windows event loop
 ------------------
 
index 0909352..14e3def 100644 (file)
@@ -4,6 +4,8 @@
 Synchronization primitives
 ==========================
 
+**Source code:** :source:`Lib/asyncio/locks.py`
+
 Locks:
 
 * :class:`Lock`
index 804f192..5298c11 100644 (file)
@@ -3,6 +3,10 @@
 Tasks and coroutines
 ====================
 
+**Source code:** :source:`Lib/asyncio/tasks.py`
+
+**Source code:** :source:`Lib/asyncio/coroutines.py`
+
 .. _coroutine:
 
 Coroutines
index 0ecd89b..b9c1868 100644 (file)
@@ -554,19 +554,19 @@ define in order to be compatible with the Python codec registry.
       if necessary, to reset the encoder and to get the output.
 
 
-.. method:: IncrementalEncoder.getstate()
+   .. method:: getstate()
 
-   Return the current state of the encoder which must be an integer. The
-   implementation should make sure that ``0`` is the most common state. (States
-   that are more complicated than integers can be converted into an integer by
-   marshaling/pickling the state and encoding the bytes of the resulting string
-   into an integer).
+      Return the current state of the encoder which must be an integer. The
+      implementation should make sure that ``0`` is the most common
+      state. (States that are more complicated than integers can be converted
+      into an integer by marshaling/pickling the state and encoding the bytes
+      of the resulting string into an integer).
 
 
-.. method:: IncrementalEncoder.setstate(state)
+   .. method:: setstate(state)
 
-   Set the state of the encoder to *state*. *state* must be an encoder state
-   returned by :meth:`getstate`.
+      Set the state of the encoder to *state*. *state* must be an encoder state
+      returned by :meth:`getstate`.
 
 
 .. _incremental-decoder-objects:
index 6621f4a..bd4c94f 100644 (file)
@@ -81,9 +81,24 @@ are always available.  They are listed here in alphabetical order.
 
 .. function:: bin(x)
 
-   Convert an integer number to a binary string. The result is a valid Python
-   expression.  If *x* is not a Python :class:`int` object, it has to define an
-   :meth:`__index__` method that returns an integer.
+   Convert an integer number to a binary string prefixed with "0b". The result
+   is a valid Python expression. If *x* is not a Python :class:`int` object, it
+   has to define an :meth:`__index__` method that returns an integer. Some
+   examples:
+
+      >>> bin(3)
+      '0b11'
+      >>> bin(-10)
+      '-0b1010'
+
+   If prefix "0b" is desired or not, you can use either of the following ways.
+
+      >>> format(14, '#b'), format(14, 'b')
+      ('0b1110', '1110')
+      >>> f'{14:#b}', f'{14:b}'
+      ('0b1110', '1110')
+
+  See also :func:`format` for more information.
 
 
 .. class:: bool([x])
@@ -635,16 +650,26 @@ are always available.  They are listed here in alphabetical order.
 
 .. function:: hex(x)
 
-   Convert an integer number to a lowercase hexadecimal string
-   prefixed with "0x", for example:
+   Convert an integer number to a lowercase hexadecimal string prefixed with
+   "0x". If x is not a Python :class:`int` object, it has to define an
+   __index__() method that returns an integer. Some examples:
 
       >>> hex(255)
       '0xff'
       >>> hex(-42)
       '-0x2a'
 
-   If x is not a Python :class:`int` object, it has to define an __index__()
-   method that returns an integer.
+   If you want to convert an integer number to an uppercase or lower hexadecimal
+   string with prefix or not, you can use either of the following ways:
+
+     >>> '%#x' % 255, '%x' % 255, '%X' % 255
+     ('0xff', 'ff', 'FF')
+     >>> format(255, '#x'), format(255, 'x'), format(255, 'X')
+     ('0xff', 'ff', 'FF')
+     >>> f'{255:#x}', f'{255:x}', f'{255:X}'
+     ('0xff', 'ff', 'FF')
+
+   See also :func:`format` for more information.
 
    See also :func:`int` for converting a hexadecimal string to an
    integer using a base of 16.
@@ -878,10 +903,27 @@ are always available.  They are listed here in alphabetical order.
 
 .. function:: oct(x)
 
-   Convert an integer number to an octal string.  The result is a valid Python
-   expression.  If *x* is not a Python :class:`int` object, it has to define an
-   :meth:`__index__` method that returns an integer.
+  Convert an integer number to an octal string prefixed with "0o".  The result
+  is a valid Python expression. If *x* is not a Python :class:`int` object, it
+  has to define an :meth:`__index__` method that returns an integer. For
+  example:
+
+      >>> oct(8)
+      '0o10'
+      >>> oct(-56)
+      '-0o70'
+
+  If you want to convert an integer number to octal string either with prefix
+  "0o" or not, you can use either of the following ways.
+
+      >>> '%#o' % 10, '%o' % 10
+      ('0o12', '12')
+      >>> format(10, '#o'), format(10, 'o')
+      ('0o12', '12')
+      >>> f'{10:#o}', f'{10:o}'
+      ('0o12', '12')
 
+  See also :func:`format` for more information.
 
    .. index::
       single: file object; open() built-in function
@@ -1319,7 +1361,7 @@ are always available.  They are listed here in alphabetical order.
    :func:`itertools.islice` for an alternate version that returns an iterator.
 
 
-.. function:: sorted(iterable[, key][, reverse])
+.. function:: sorted(iterable, *, key=None, reverse=False)
 
    Return a new sorted list from the items in *iterable*.
 
index 3a87bf5..053d9d3 100644 (file)
@@ -48,9 +48,10 @@ class-based API instead.
 
 .. function:: bind_textdomain_codeset(domain, codeset=None)
 
-   Bind the *domain* to *codeset*, changing the encoding of strings returned by the
-   :func:`gettext` family of functions. If *codeset* is omitted, then the current
-   binding is returned.
+   Bind the *domain* to *codeset*, changing the encoding of byte strings
+   returned by the :func:`lgettext`, :func:`ldgettext`, :func:`lngettext`
+   and :func:`ldngettext` functions.
+   If *codeset* is omitted, then the current binding is returned.
 
 
 .. function:: textdomain(domain=None)
@@ -67,28 +68,14 @@ class-based API instead.
    :func:`_` in the local namespace (see examples below).
 
 
-.. function:: lgettext(message)
-
-   Equivalent to :func:`gettext`, but the translation is returned in the
-   preferred system encoding, if no other encoding was explicitly set with
-   :func:`bind_textdomain_codeset`.
-
-
 .. function:: dgettext(domain, message)
 
-   Like :func:`gettext`, but look the message up in the specified *domain*.
-
-
-.. function:: ldgettext(domain, message)
-
-   Equivalent to :func:`dgettext`, but the translation is returned in the
-   preferred system encoding, if no other encoding was explicitly set with
-   :func:`bind_textdomain_codeset`.
+   Like :func:`.gettext`, but look the message up in the specified *domain*.
 
 
 .. function:: ngettext(singular, plural, n)
 
-   Like :func:`gettext`, but consider plural forms. If a translation is found,
+   Like :func:`.gettext`, but consider plural forms. If a translation is found,
    apply the plural formula to *n*, and return the resulting message (some
    languages have more than two plural forms). If no translation is found, return
    *singular* if *n* is 1; return *plural* otherwise.
@@ -101,24 +88,33 @@ class-based API instead.
    formulas for a variety of languages.
 
 
-.. function:: lngettext(singular, plural, n)
-
-   Equivalent to :func:`ngettext`, but the translation is returned in the
-   preferred system encoding, if no other encoding was explicitly set with
-   :func:`bind_textdomain_codeset`.
-
-
 .. function:: dngettext(domain, singular, plural, n)
 
    Like :func:`ngettext`, but look the message up in the specified *domain*.
 
 
+.. function:: lgettext(message)
+.. function:: ldgettext(domain, message)
+.. function:: lngettext(singular, plural, n)
 .. function:: ldngettext(domain, singular, plural, n)
 
-   Equivalent to :func:`dngettext`, but the translation is returned in the
-   preferred system encoding, if no other encoding was explicitly set with
+   Equivalent to the corresponding functions without the ``l`` prefix
+   (:func:`.gettext`, :func:`dgettext`, :func:`ngettext` and :func:`dngettext`),
+   but the translation is returned as a byte string encoded in the preferred
+   system encoding if no other encoding was explicitly set with
    :func:`bind_textdomain_codeset`.
 
+   .. warning::
+
+      These functions should be avoided in Python 3, because they return
+      encoded bytes.  It's much better to use alternatives which return
+      Unicode strings instead, since most Python applications will want to
+      manipulate human readable text as strings instead of bytes.  Further,
+      it's possible that you may get unexpected Unicode-related exceptions
+      if there are encoding problems with the translated strings.  It is
+      possible that the ``l*()`` functions will be deprecated in future Python
+      versions due to their inherent problems and limitations.
+
 
 Note that GNU :program:`gettext` also defines a :func:`dcgettext` method, but
 this was deemed not useful and so it is currently unimplemented.
@@ -179,8 +175,9 @@ class can also install themselves in the built-in namespace as the function
    names are cached.  The actual class instantiated is either *class_* if
    provided, otherwise :class:`GNUTranslations`.  The class's constructor must
    take a single :term:`file object` argument.  If provided, *codeset* will change
-   the charset used to encode translated strings in the :meth:`lgettext` and
-   :meth:`lngettext` methods.
+   the charset used to encode translated strings in the
+   :meth:`~NullTranslations.lgettext` and :meth:`~NullTranslations.lngettext`
+   methods.
 
    If multiple files are found, later files are used as fallbacks for earlier ones.
    To allow setting the fallback, :func:`copy.copy` is used to clone each
@@ -250,26 +247,29 @@ are the methods of :class:`NullTranslations`:
 
    .. method:: gettext(message)
 
-      If a fallback has been set, forward :meth:`gettext` to the fallback.
-      Otherwise, return the translated message.  Overridden in derived classes.
-
-
-   .. method:: lgettext(message)
-
-      If a fallback has been set, forward :meth:`lgettext` to the fallback.
-      Otherwise, return the translated message.  Overridden in derived classes.
+      If a fallback has been set, forward :meth:`.gettext` to the fallback.
+      Otherwise, return *message*.  Overridden in derived classes.
 
 
    .. method:: ngettext(singular, plural, n)
 
       If a fallback has been set, forward :meth:`ngettext` to the fallback.
-      Otherwise, return the translated message.  Overridden in derived classes.
+      Otherwise, return *singular* if *n* is 1; return *plural* otherwise.
+      Overridden in derived classes.
 
 
+   .. method:: lgettext(message)
    .. method:: lngettext(singular, plural, n)
 
-      If a fallback has been set, forward :meth:`lngettext` to the fallback.
-      Otherwise, return the translated message.  Overridden in derived classes.
+      Equivalent to :meth:`.gettext` and :meth:`ngettext`, but the translation
+      is returned as a byte string encoded in the preferred system encoding
+      if no encoding was explicitly set with :meth:`set_output_charset`.
+      Overridden in derived classes.
+
+      .. warning::
+
+         These methods should be avoided in Python 3.  See the warning for the
+         :func:`lgettext` function.
 
 
    .. method:: info()
@@ -279,32 +279,28 @@ are the methods of :class:`NullTranslations`:
 
    .. method:: charset()
 
-      Return the "protected" :attr:`_charset` variable, which is the encoding of
-      the message catalog file.
+      Return the encoding of the message catalog file.
 
 
    .. method:: output_charset()
 
-      Return the "protected" :attr:`_output_charset` variable, which defines the
-      encoding used to return translated messages in :meth:`lgettext` and
-      :meth:`lngettext`.
+      Return the encoding used to return translated messages in :meth:`.lgettext`
+      and :meth:`.lngettext`.
 
 
    .. method:: set_output_charset(charset)
 
-      Change the "protected" :attr:`_output_charset` variable, which defines the
-      encoding used to return translated messages.
+      Change the encoding used to return translated messages.
 
 
    .. method:: install(names=None)
 
-      This method installs :meth:`self.gettext` into the built-in namespace,
+      This method installs :meth:`.gettext` into the built-in namespace,
       binding it to ``_``.
 
       If the *names* parameter is given, it must be a sequence containing the
       names of functions you want to install in the builtins namespace in
-      addition to :func:`_`.  Supported names are ``'gettext'`` (bound to
-      :meth:`self.gettext`), ``'ngettext'`` (bound to :meth:`self.ngettext`),
+      addition to :func:`_`.  Supported names are ``'gettext'``, ``'ngettext'``,
       ``'lgettext'`` and ``'lngettext'``.
 
       Note that this is only one way, albeit the most convenient way, to make
@@ -349,49 +345,52 @@ If the :file:`.mo` file's magic number is invalid, the major version number is
 unexpected, or if other problems occur while reading the file, instantiating a
 :class:`GNUTranslations` class can raise :exc:`OSError`.
 
-The following methods are overridden from the base class implementation:
-
+.. class:: GNUTranslations
 
-.. method:: GNUTranslations.gettext(message)
+   The following methods are overridden from the base class implementation:
 
-   Look up the *message* id in the catalog and return the corresponding message
-   string, as a Unicode string.  If there is no entry in the catalog for the
-   *message* id, and a fallback has been set, the look up is forwarded to the
-   fallback's :meth:`gettext` method.  Otherwise, the *message* id is returned.
+   .. method:: gettext(message)
 
+      Look up the *message* id in the catalog and return the corresponding message
+      string, as a Unicode string.  If there is no entry in the catalog for the
+      *message* id, and a fallback has been set, the look up is forwarded to the
+      fallback's :meth:`~NullTranslations.gettext` method.  Otherwise, the
+      *message* id is returned.
 
-.. method:: GNUTranslations.lgettext(message)
 
-   Equivalent to :meth:`gettext`, but the translation is returned as a
-   bytestring encoded in the selected output charset, or in the preferred system
-   encoding if no encoding was explicitly set with :meth:`set_output_charset`.
+   .. method:: ngettext(singular, plural, n)
 
+      Do a plural-forms lookup of a message id.  *singular* is used as the message id
+      for purposes of lookup in the catalog, while *n* is used to determine which
+      plural form to use.  The returned message string is a Unicode string.
 
-.. method:: GNUTranslations.ngettext(singular, plural, n)
+      If the message id is not found in the catalog, and a fallback is specified,
+      the request is forwarded to the fallback's :meth:`~NullTranslations.ngettext`
+      method.  Otherwise, when *n* is 1 *singular* is returned, and *plural* is
+      returned in all other cases.
 
-   Do a plural-forms lookup of a message id.  *singular* is used as the message id
-   for purposes of lookup in the catalog, while *n* is used to determine which
-   plural form to use.  The returned message string is a Unicode string.
+      Here is an example::
 
-   If the message id is not found in the catalog, and a fallback is specified, the
-   request is forwarded to the fallback's :meth:`ngettext` method.  Otherwise, when
-   *n* is 1 *singular* is returned, and *plural* is returned in all other cases.
+         n = len(os.listdir('.'))
+         cat = GNUTranslations(somefile)
+         message = cat.ngettext(
+             'There is %(num)d file in this directory',
+             'There are %(num)d files in this directory',
+             n) % {'num': n}
 
-   Here is an example::
 
-      n = len(os.listdir('.'))
-      cat = GNUTranslations(somefile)
-      message = cat.ngettext(
-          'There is %(num)d file in this directory',
-          'There are %(num)d files in this directory',
-          n) % {'num': n}
+   .. method:: lgettext(message)
+   .. method:: lngettext(singular, plural, n)
 
+      Equivalent to :meth:`.gettext` and :meth:`.ngettext`, but the translation
+      is returned as a byte string encoded in the preferred system encoding
+      if no encoding  was explicitly set with
+      :meth:`~NullTranslations.set_output_charset`.
 
-.. method:: GNUTranslations.lngettext(singular, plural, n)
+      .. warning::
 
-   Equivalent to :meth:`gettext`, but the translation is returned as a
-   bytestring encoded in the selected output charset, or in the preferred system
-   encoding if no encoding was explicitly set with :meth:`set_output_charset`.
+         These methods should be avoided in Python 3.  See the warning for the
+         :func:`lgettext` function.
 
 
 Solaris message catalog support
@@ -509,7 +508,7 @@ module::
 
    import gettext
    t = gettext.translation('spam', '/usr/share/locale')
-   _ = t.lgettext
+   _ = t.gettext
 
 
 Localizing your application
index 9d56356..725dce6 100644 (file)
@@ -506,8 +506,9 @@ to users and later verify them to make sure they weren't tampered with::
     >>> AUTH_SIZE = 16
     >>>
     >>> def sign(cookie):
-    ...     h = blake2b(data=cookie, digest_size=AUTH_SIZE, key=SECRET_KEY)
-    ...     return h.hexdigest()
+    ...     h = blake2b(digest_size=AUTH_SIZE, key=SECRET_KEY)
+    ...     h.update(cookie)
+    ...     return h.hexdigest().encode('utf-8')
     >>>
     >>> cookie = b'user:vatrogasac'
     >>> sig = sign(cookie)
@@ -517,7 +518,7 @@ to users and later verify them to make sure they weren't tampered with::
     True
     >>> compare_digest(b'user:policajac', sig)
     False
-    >>> compare_digesty(cookie, '0102030405060708090a0b0c0d0e0f00')
+    >>> compare_digest(cookie, b'0102030405060708090a0b0c0d0e0f00')
     False
 
 Even though there's a native keyed hashing mode, BLAKE2 can, of course, be used
index a945b6d..af15359 100644 (file)
@@ -190,7 +190,9 @@ Format Paragraph
    paragraph will be formatted to less than N columns, where N defaults to 72.
 
 Strip trailing whitespace
-   Remove any space characters after the last non-space character of a line.
+   Remove trailing space and other whitespace characters after the last
+   non-whitespace character of a line by applying str.rstrip to each line,
+   including lines within multiline strings.
 
 .. index::
    single: Run script
@@ -599,15 +601,15 @@ starting from a console (``python -m idlelib)`` and see if a message appears.
 IDLE-console differences
 ^^^^^^^^^^^^^^^^^^^^^^^^
 
-As much as possible, the result of executing Python code with IDLE is the
-same as executing the same code in a console window.  However, the different
-interface and operation occasionally affect visible results.  For instance,
-``sys.modules`` starts with more entries.
+With rare exceptions, the result of executing Python code with IDLE is
+intended to be the same as executing the same code in a console window.
+However, the different interface and operation occasionally affect
+visible results.  For instance, ``sys.modules`` starts with more entries.
 
 IDLE also replaces ``sys.stdin``, ``sys.stdout``, and ``sys.stderr`` with
 objects that get input from and send output to the Shell window.
-When this window has the focus, it controls the keyboard and screen.
-This is normally transparent, but functions that directly access the keyboard
+When Shell has the focus, it controls the keyboard and screen.  This is
+normally transparent, but functions that directly access the keyboard
 and screen will not work.  If ``sys`` is reset with ``importlib.reload(sys)``,
 IDLE's changes are lost and things like ``input``, ``raw_input``, and
 ``print`` will not work correctly.
@@ -617,6 +619,29 @@ Some consoles only work with a single physical line at a time.  IDLE uses
 ``exec`` to run each statement.  As a result, ``'__builtins__'`` is always
 defined for each statement.
 
+Developing tkinter applications
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+IDLE is intentionally different from standard Python in order to
+facilitate development of tkinter programs.  Enter ``import tkinter as tk;
+root = tk.Tk()`` in standard Python and nothing appears.  Enter the same
+in IDLE and a tk window appears.  In standard Python, one must also enter
+``root.update()`` to see the window.  IDLE does the equivalent in the
+background, about 20 times a second, which is about every 50 milleseconds.
+Next enter ``b = tk.Button(root, text='button'); b.pack()``.  Again,
+nothing visibly changes in standard Python until one enters ``root.update()``.
+
+Most tkinter programs run ``root.mainloop()``, which usually does not
+return until the tk app is destroyed.  If the program is run with
+``python -i`` or from an IDLE editor, a ``>>>`` shell prompt does not
+appear until ``mainloop()`` returns, at which time there is nothing left
+to interact with.
+
+When running a tkinter program from an IDLE editor, one can comment out
+the mainloop call.  One then gets a shell prompt immediately and can
+interact with the live application.  One just has to remember to
+re-enable the mainloop call when running in standard Python.
+
 Running without a subprocess
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -671,24 +696,7 @@ Extensions
 ^^^^^^^^^^
 
 IDLE contains an extension facility.  Preferences for extensions can be
-changed with Configure Extensions. See the beginning of config-extensions.def
-in the idlelib directory for further information.  The default extensions
-are currently:
-
-* FormatParagraph
-
-* AutoExpand
-
-* ZoomHeight
-
-* ScriptBinding
-
-* CallTips
-
-* ParenMatch
-
-* AutoComplete
-
-* CodeContext
-
-* RstripExtension
+changed with the Extensions tab of the preferences dialog. See the
+beginning of config-extensions.def in the idlelib directory for further
+information.  The only current default extension is zzdummy, an example
+also used for testing.
index 1fd5698..6406e30 100644 (file)
@@ -32,7 +32,7 @@ generically as an :term:`importer`) to participate in the import process.
     :ref:`import`
         The language reference for the :keyword:`import` statement.
 
-    `Packages specification <http://legacy.python.org/doc/essays/packages.html>`__
+    `Packages specification <https://www.python.org/doc/essays/packages/>`__
         Original specification of packages. Some semantics have changed since
         the writing of this document (e.g. redirecting based on ``None``
         in :data:`sys.modules`).
index fdbdcb1..829218d 100644 (file)
@@ -43,7 +43,7 @@ Encoding basic Python object hierarchies::
 Compact encoding::
 
     >>> import json
-    >>> json.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',', ':'))
+    >>> json.dumps([1, 2, 3, {'4': 5, '6': 7}], separators=(',', ':'))
     '[1,2,3,{"4":5,"6":7}]'
 
 Pretty printing::
index 96d1424..2f770b6 100644 (file)
@@ -1024,7 +1024,7 @@ Connection objects are usually created using :func:`Pipe` -- see also
    .. method:: recv()
 
       Return an object sent from the other end of the connection using
-      :meth:`send`.  Blocks until there its something to receive.  Raises
+      :meth:`send`.  Blocks until there is something to receive.  Raises
       :exc:`EOFError` if there is nothing left to receive
       and the other end was closed.
 
index 5ae304d..56fc4ed 100644 (file)
@@ -490,9 +490,10 @@ form.
 
 .. function:: compile(pattern, flags=0)
 
-   Compile a regular expression pattern into a regular expression object, which
-   can be used for matching using its :func:`~regex.match` and
-   :func:`~regex.search` methods, described below.
+   Compile a regular expression pattern into a :ref:`regular expression object
+   <re-objects>`, which can be used for matching using its
+   :func:`~regex.match`, :func:`~regex.search` and other methods, described
+   below.
 
    The expression's behaviour can be modified by specifying a *flags* value.
    Values can be any of the following variables, combined using bitwise OR (the
@@ -541,9 +542,11 @@ form.
 .. data:: I
           IGNORECASE
 
-   Perform case-insensitive matching; expressions like ``[A-Z]`` will match
-   lowercase letters, too.  This is not affected by the current locale
-   and works for Unicode characters as expected.
+   Perform case-insensitive matching; expressions like ``[A-Z]`` will also
+   match lowercase letters.  The current locale does not change the effect of
+   this flag.  Full Unicode matching (such as ``Ü`` matching ``ü``) also
+   works unless the :const:`re.ASCII` flag is also used to disable non-ASCII
+   matches.
 
 
 .. data:: L
index 6c30a74..d7e0467 100644 (file)
@@ -193,11 +193,11 @@ instead.
      .. table::
 
        ========================  ============  ============  =============  =========  ===========  ===========
-        *client* / **server**    **SSLv2**     **SSLv3**     **TLS**        **TLSv1**  **TLSv1.1**  **TLSv1.2**
+        *client* / **server**    **SSLv2**     **SSLv3**     **TLS** [3]_   **TLSv1**  **TLSv1.1**  **TLSv1.2**
        ------------------------  ------------  ------------  -------------  ---------  -----------  -----------
         *SSLv2*                    yes           no            no [1]_        no         no         no
         *SSLv3*                    no            yes           no [2]_        no         no         no
-        *TLS* (*SSLv23*)           no [1]_       no [2]_       yes            yes        yes        yes
+        *TLS* (*SSLv23*) [3]_      no [1]_       no [2]_       yes            yes        yes        yes
         *TLSv1*                    no            no            yes            yes        no         no
         *TLSv1.1*                  no            no            yes            no         yes        no
         *TLSv1.2*                  no            no            yes            no         no         yes
@@ -206,6 +206,9 @@ instead.
    .. rubric:: Footnotes
    .. [1] :class:`SSLContext` disables SSLv2 with :data:`OP_NO_SSLv2` by default.
    .. [2] :class:`SSLContext` disables SSLv3 with :data:`OP_NO_SSLv3` by default.
+   .. [3] TLS 1.3 protocol will be available with :data:`PROTOCOL_TLS` in
+      OpenSSL >= 1.1.1. There is no dedicated PROTOCOL constant for just
+      TLS 1.3.
 
    .. note::
 
@@ -294,6 +297,11 @@ purposes.
 
      3DES was dropped from the default cipher string.
 
+   .. versionchanged:: 3.6.3
+
+     TLS 1.3 cipher suites TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384,
+     and TLS_CHACHA20_POLY1305_SHA256 were added to the default cipher string.
+
 
 Random generation
 ^^^^^^^^^^^^^^^^^
@@ -760,6 +768,16 @@ Constants
 
    .. versionadded:: 3.4
 
+.. data:: OP_NO_TLSv1_3
+
+   Prevents a TLSv1.3 connection. This option is only applicable in conjunction
+   with :const:`PROTOCOL_TLS`. It prevents the peers from choosing TLSv1.3 as
+   the protocol version. TLS 1.3 is available with OpenSSL 1.1.1 or later.
+   When Python has been compiled against an older version of OpenSSL, the
+   flag defaults to *0*.
+
+   .. versionadded:: 3.6.3
+
 .. data:: OP_CIPHER_SERVER_PREFERENCE
 
    Use the server's cipher ordering preference, rather than the client's.
@@ -834,6 +852,12 @@ Constants
 
    .. versionadded:: 3.3
 
+.. data:: HAS_TLSv1_3
+
+   Whether the OpenSSL library has built-in support for the TLS 1.3 protocol.
+
+   .. versionadded:: 3.6.3
+
 .. data:: CHANNEL_BINDING_TYPES
 
    List of supported TLS channel binding types.  Strings in this list
@@ -1442,8 +1466,9 @@ to speed up repeated connections from the same clients.
    This method will raise :exc:`NotImplementedError` if :data:`HAS_ALPN` is
    False.
 
-   OpenSSL 1.1.0+ will abort the handshake and raise :exc:`SSLError` when
-   both sides support ALPN but cannot agree on a protocol.
+   OpenSSL 1.1.0 to 1.1.0e will abort the handshake and raise :exc:`SSLError`
+   when both sides support ALPN but cannot agree on a protocol. 1.1.0f+
+   behaves like 1.0.2, :meth:`SSLSocket.selected_alpn_protocol` returns None.
 
    .. versionadded:: 3.5
 
@@ -2320,3 +2345,9 @@ successful call of :func:`~ssl.RAND_add`, :func:`~ssl.RAND_bytes` or
 
    `IANA TLS: Transport Layer Security (TLS) Parameters <https://www.iana.org/assignments/tls-parameters/tls-parameters.xml>`_
        IANA
+
+   `RFC 7525: Recommendations for Secure Use of Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS) <https://tools.ietf.org/html/rfc7525>`_
+       IETF
+
+   `Mozilla's Server Side TLS recommendations <https://wiki.mozilla.org/Security/Server_Side_TLS>`_
+       Mozilla
index 083fe53..b8c4d59 100644 (file)
@@ -39,31 +39,26 @@ Truth Value Testing
    single: false
 
 Any object can be tested for truth value, for use in an :keyword:`if` or
-:keyword:`while` condition or as operand of the Boolean operations below. The
-following values are considered false:
+:keyword:`while` condition or as operand of the Boolean operations below.
 
-  .. index:: single: None (Built-in object)
-
-* ``None``
-
-  .. index:: single: False (Built-in object)
-
-* ``False``
-
-* zero of any numeric type, for example, ``0``, ``0.0``, ``0j``.
+.. index:: single: true
 
-* any empty sequence, for example, ``''``, ``()``, ``[]``.
+By default, an object is considered true unless its class defines either a
+:meth:`__bool__` method that returns ``False`` or a :meth:`__len__` method that
+returns zero, when called with the object. [1]_  Here are most of the built-in
+objects considered false:
 
-* any empty mapping, for example, ``{}``.
+  .. index::
+     single: None (Built-in object)
+     single: False (Built-in object)
 
-* instances of user-defined classes, if the class defines a :meth:`__bool__` or
-  :meth:`__len__` method, when that method returns the integer zero or
-  :class:`bool` value ``False``. [1]_
+* constants defined to be false: ``None`` and ``False``.
 
-.. index:: single: true
+* zero of any numeric type: ``0``, ``0.0``, ``0j``, ``Decimal(0)``,
+  ``Fraction(0, 1)``
 
-All other values are considered true --- so objects of many types are always
-true.
+* empty sequences and collections: ``''``, ``()``, ``[]``, ``{}``, ``set()``,
+  ``range(0)``
 
 .. index::
    operator: or
@@ -354,7 +349,7 @@ Notes:
    The numeric literals accepted include the digits ``0`` to ``9`` or any
    Unicode equivalent (code points with the ``Nd`` property).
 
-   See http://www.unicode.org/Public/8.0.0/ucd/extracted/DerivedNumericType.txt
+   See http://www.unicode.org/Public/9.0.0/ucd/extracted/DerivedNumericType.txt
    for a complete list of code points with the ``Nd`` property.
 
 
@@ -1161,7 +1156,7 @@ application).
    :ref:`mutable <typesseq-mutable>` sequence operations. Lists also provide the
    following additional method:
 
-   .. method:: list.sort(*, key=None, reverse=None)
+   .. method:: list.sort(*, key=None, reverse=False)
 
       This method sorts the list in place, using only ``<`` comparisons
       between items. Exceptions are not suppressed - if any comparison operations
index 548e4a6..27f3e82 100644 (file)
@@ -38,7 +38,7 @@ compatibility with older versions, see the :ref:`call-function-trio` section.
 
 
 .. function:: run(args, *, stdin=None, input=None, stdout=None, stderr=None,\
-                  shell=False, timeout=None, check=False, \
+                  shell=False, cwd=None, timeout=None, check=False, \
                   encoding=None, errors=None)
 
    Run the command described by *args*.  Wait for command to complete, then
@@ -856,7 +856,7 @@ Prior to Python 3.5, these three functions comprised the high level API to
 subprocess. You can now use :func:`run` in many cases, but lots of existing code
 calls these functions.
 
-.. function:: call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)
+.. function:: call(args, *, stdin=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None)
 
    Run the command described by *args*.  Wait for command to complete, then
    return the :attr:`~Popen.returncode` attribute.
@@ -882,7 +882,7 @@ calls these functions.
    .. versionchanged:: 3.3
       *timeout* was added.
 
-.. function:: check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)
+.. function:: check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None)
 
    Run command with arguments.  Wait for command to complete. If the return
    code was zero then return, otherwise raise :exc:`CalledProcessError`. The
@@ -912,7 +912,7 @@ calls these functions.
 
 
 .. function:: check_output(args, *, stdin=None, stderr=None, shell=False, \
-                           encoding=None, errors=None, \
+                           cwd=None, encoding=None, errors=None, \
                            universal_newlines=False, timeout=None)
 
    Run command with arguments and return its output.
@@ -1166,27 +1166,32 @@ handling consistency are valid for these functions.
 
 .. function:: getstatusoutput(cmd)
 
-   Return ``(status, output)`` of executing *cmd* in a shell.
+   Return ``(exitcode, output)`` of executing *cmd* in a shell.
 
    Execute the string *cmd* in a shell with :meth:`Popen.check_output` and
-   return a 2-tuple ``(status, output)``. The locale encoding is used;
+   return a 2-tuple ``(exitcode, output)``. The locale encoding is used;
    see the notes on :ref:`frequently-used-arguments` for more details.
 
    A trailing newline is stripped from the output.
-   The exit status for the command can be interpreted
-   according to the rules for the C function :c:func:`wait`.  Example::
+   The exit code for the command can be interpreted as the return code
+   of subprocess.  Example::
 
       >>> subprocess.getstatusoutput('ls /bin/ls')
       (0, '/bin/ls')
       >>> subprocess.getstatusoutput('cat /bin/junk')
-      (256, 'cat: /bin/junk: No such file or directory')
+      (1, 'cat: /bin/junk: No such file or directory')
       >>> subprocess.getstatusoutput('/bin/junk')
-      (256, 'sh: /bin/junk: not found')
+      (127, 'sh: /bin/junk: not found')
+      >>> subprocess.getstatusoutput('/bin/kill $$')
+      (-15, '')
 
    Availability: POSIX & Windows
 
    .. versionchanged:: 3.3.4
-      Windows support added
+      Windows support was added.
+
+      The function now returns (exitcode, output) instead of (status, output)
+      as it did in Python 3.3.3 and earlier.  See :func:`WEXITSTATUS`.
 
 
 .. function:: getoutput(cmd)
index cda859f..021e29e 100644 (file)
@@ -875,8 +875,8 @@ Barrier Objects
 This class provides a simple synchronization primitive for use by a fixed number
 of threads that need to wait for each other.  Each of the threads tries to pass
 the barrier by calling the :meth:`~Barrier.wait` method and will block until
-all of the threads have made the call.  At this points, the threads are released
-simultaneously.
+all of the threads have made their :meth:`~Barrier.wait` calls. At this point,
+the threads are released simultaneously.
 
 The barrier can be reused any number of times for the same number of threads.
 
index ff55aac..afed36b 100644 (file)
@@ -16,8 +16,9 @@ implemented in Python.  The scanner in this module returns comments as tokens
 as well, making it useful for implementing "pretty-printers," including
 colorizers for on-screen displays.
 
-To simplify token stream handling, all :ref:`operators` and :ref:`delimiters`
-tokens are returned using the generic :data:`token.OP` token type.  The exact
+To simplify token stream handling, all :ref:`operator <operators>` and
+:ref:`delimiter <delimiters>` tokens and :data:`Ellipsis` are returned using
+the generic :data:`~token.OP` token type.  The exact
 type can be determined by checking the ``exact_type`` property on the
 :term:`named tuple` returned from :func:`tokenize.tokenize`.
 
index 31761be..2b5d3e5 100644 (file)
@@ -2368,7 +2368,7 @@ The demo scripts are:
 | wikipedia      | a pattern from the wikipedia | :func:`clone`,        |
 |                | article on turtle graphics   | :func:`undo`          |
 +----------------+------------------------------+-----------------------+
-| yingyang       | another elementary example   | :func:`circle`        |
+| yinyang        | another elementary example   | :func:`circle`        |
 +----------------+------------------------------+-----------------------+
 
 Have fun!
index 1780739..1e48fec 100644 (file)
@@ -939,7 +939,7 @@ The module defines the following classes, functions and decorators:
 
        Union[int, str] == Union[str, int]
 
-   * When a class and its subclass are present, the former is skipped, e.g.::
+   * When a class and its subclass are present, the latter is skipped, e.g.::
 
        Union[int, object] == object
 
index b1a3738..968b20b 100644 (file)
@@ -5,8 +5,30 @@ pushd %~dp0
 \r
 set this=%~n0\r
 \r
-if "%SPHINXBUILD%" EQU "" set SPHINXBUILD=sphinx-build\r
-if "%PYTHON%" EQU "" set PYTHON=py\r
+call ..\PCBuild\find_python.bat %PYTHON%\r
+if not defined SPHINXBUILD if defined PYTHON (\r
+    %PYTHON% -c "import sphinx" > nul 2> nul\r
+    if errorlevel 1 (\r
+        echo Installing sphinx with %PYTHON%\r
+        %PYTHON% -m pip install sphinx\r
+        if errorlevel 1 exit /B\r
+    )\r
+    set SPHINXBUILD=%PYTHON% -c "import sphinx, sys; sys.argv[0] = 'sphinx-build'; sphinx.main()"\r
+)\r
+\r
+if not defined BLURB if defined PYTHON (\r
+    %PYTHON% -c "import blurb" > nul 2> nul\r
+    if errorlevel 1 (\r
+        echo Installing blurb with %PYTHON%\r
+        %PYTHON% -m pip install blurb\r
+        if errorlevel 1 exit /B\r
+    )\r
+    set BLURB=%PYTHON% -m blurb\r
+)\r
+\r
+if not defined PYTHON set PYTHON=py\r
+if not defined SPHINXBUILD set SPHINXBUILD=sphinx-build\r
+if not defined BLURB set BLURB=blurb\r
 \r
 if "%1" NEQ "htmlhelp" goto :skiphhcsearch\r
 if exist "%HTMLHELP%" goto :skiphhcsearch\r
@@ -36,7 +58,7 @@ if "%1" EQU "help" goto help
 if "%1" EQU "check" goto check\r
 if "%1" EQU "serve" goto serve\r
 if "%1" == "clean" (\r
-    rmdir /q /s %BUILDDIR%\r
+    rmdir /q /s "%BUILDDIR%"\r
     goto end\r
 )\r
 \r
@@ -85,13 +107,28 @@ echo.be passed by setting the SPHINXOPTS environment variable.
 goto end\r
 \r
 :build\r
+if not exist "%BUILDDIR%" mkdir "%BUILDDIR%"\r
+\r
+if exist ..\Misc\NEWS (\r
+    echo.Copying Misc\NEWS to build\NEWS\r
+    copy ..\Misc\NEWS build\NEWS > nul\r
+) else if exist ..\Misc\NEWS.D (\r
+    if defined BLURB (\r
+        echo.Merging Misc/NEWS with %BLURB%\r
+        %BLURB% merge -f build\NEWS\r
+    ) else (\r
+        echo.No Misc/NEWS file and Blurb is not available.\r
+        exit /B 1\r
+    )\r
+)\r
+\r
 if NOT "%PAPER%" == "" (\r
     set SPHINXOPTS=-D latex_elements.papersize=%PAPER% %SPHINXOPTS%\r
 )\r
-cmd /C %SPHINXBUILD% %SPHINXOPTS% -b%1 -dbuild\doctrees . %BUILDDIR%\%*\r
+cmd /S /C "%SPHINXBUILD% %SPHINXOPTS% -b%1 -dbuild\doctrees . "%BUILDDIR%\%1" %2 %3 %4 %5 %6 %7 %8 %9"\r
 \r
 if "%1" EQU "htmlhelp" (\r
-    cmd /C "%HTMLHELP%" build\htmlhelp\python%DISTVERSION:.=%.hhp\r
+    "%HTMLHELP%" "%BUILDDIR%\htmlhelp\python%DISTVERSION:.=%.hhp"\r
     rem hhc.exe seems to always exit with code 1, reset to 0 for less than 2\r
     if not errorlevel 2 cmd /C exit /b 0\r
 )\r
@@ -111,19 +148,19 @@ if NOT "%2" EQU "" (
 )\r
 cmd /C %this% html\r
 \r
-if EXIST %BUILDDIR%\html\index.html (\r
-    echo.Opening %BUILDDIR%\html\index.html in the default web browser...\r
-    start %BUILDDIR%\html\index.html\r
+if EXIST "%BUILDDIR%\html\index.html" (\r
+    echo.Opening "%BUILDDIR%\html\index.html" in the default web browser...\r
+    start "%BUILDDIR%\html\index.html"\r
 )\r
 \r
 goto end\r
 \r
 :check\r
-cmd /C %PYTHON% tools\rstlint.py -i tools\r
+cmd /S /C "%PYTHON% tools\rstlint.py -i tools"\r
 goto end\r
 \r
 :serve\r
-cmd /C %PYTHON% ..\Tools\scripts\serve.py %BUILDDIR%\html\r
+cmd /S /C "%PYTHON% ..\Tools\scripts\serve.py "%BUILDDIR%\html""\r
 goto end\r
 \r
 :end\r
index 7c140a3..230caf8 100644 (file)
@@ -1875,8 +1875,8 @@ Metaclass example
 ^^^^^^^^^^^^^^^^^
 
 The potential uses for metaclasses are boundless. Some ideas that have been
-explored include logging, interface checking, automatic delegation, automatic
-property creation, proxies, frameworks, and automatic resource
+explored include enum, logging, interface checking, automatic delegation,
+automatic property creation, proxies, frameworks, and automatic resource
 locking/synchronization.
 
 Here is an example of a metaclass that uses an :class:`collections.OrderedDict`
index d504f37..8cf16ca 100644 (file)
@@ -965,7 +965,7 @@ References
 
 The import machinery has evolved considerably since Python's early days.  The
 original `specification for packages
-<http://legacy.python.org/doc/essays/packages.html>`_ is still available to read,
+<https://www.python.org/doc/essays/packages/>`_ is still available to read,
 although some details have changed since the writing of that document.
 
 The original specification for :data:`sys.meta_path` was :pep:`302`, with
index 7f9c664..c2fd2b7 100644 (file)
@@ -313,7 +313,7 @@ The Unicode category codes mentioned above stand for:
 * *Nd* - decimal numbers
 * *Pc* - connector punctuations
 * *Other_ID_Start* - explicit list of characters in `PropList.txt
-  <http://www.unicode.org/Public/8.0.0/ucd/PropList.txt>`_ to support backwards
+  <http://www.unicode.org/Public/9.0.0/ucd/PropList.txt>`_ to support backwards
   compatibility
 * *Other_ID_Continue* - likewise
 
@@ -676,6 +676,12 @@ Some examples of formatted string literals::
    >>> value = decimal.Decimal("12.34567")
    >>> f"result: {value:{width}.{precision}}"  # nested fields
    'result:      12.35'
+   >>> today = datetime(year=2017, month=1, day=27)
+   >>> f"{today:%b %d, %Y}"  # using date format specifier
+   'January 27, 2017'
+   >>> number = 1024
+   >>> f"{number:#0x}"  # using integer format specifier
+   '0x400'
 
 A consequence of sharing the same syntax as regular string literals is
 that characters in the replacement fields must not conflict with the
@@ -875,4 +881,4 @@ occurrence outside string literals and comments is an unconditional error:
 
 .. rubric:: Footnotes
 
-.. [#] http://www.unicode.org/Public/8.0.0/ucd/NameAliases.txt
+.. [#] http://www.unicode.org/Public/9.0.0/ucd/NameAliases.txt
index 865f2ed..8f507e4 100644 (file)
@@ -15,6 +15,7 @@ from os import path
 from time import asctime
 from pprint import pformat
 from docutils.io import StringOutput
+from docutils.parsers.rst import Directive
 from docutils.utils import new_document
 
 from docutils import nodes, utils
@@ -23,7 +24,6 @@ from sphinx import addnodes
 from sphinx.builders import Builder
 from sphinx.locale import translators
 from sphinx.util.nodes import split_explicit_title
-from sphinx.util.compat import Directive
 from sphinx.writers.html import HTMLTranslator
 from sphinx.writers.text import TextWriter
 from sphinx.writers.latex import LaTeXTranslator
diff --git a/Doc/tools/static/switchers.js b/Doc/tools/static/switchers.js
new file mode 100644 (file)
index 0000000..bd31fac
--- /dev/null
@@ -0,0 +1,147 @@
+(function() {
+  'use strict';
+
+  // Parses versions in URL segments like:
+  // "3", "dev", "release/2.7" or "3.6rc2"
+  var version_regexs = [
+    '(?:\\d)',
+    '(?:\\d\\.\\d[\\w\\d\\.]*)',
+    '(?:dev)',
+    '(?:release/\\d.\\d[\\x\\d\\.]*)'];
+
+  var all_versions = {
+    '3.7': 'dev (3.7)',
+    '3.6': '3.6',
+    '3.5': '3.5',
+    '3.4': '3.4',
+    '3.3': '3.3',
+    '2.7': '2.7',
+  };
+
+  var all_languages = {
+      'en': 'English',
+      'fr': 'French',
+      'ja': 'Japanese',
+  };
+
+  function build_version_select(current_version, current_release) {
+    var buf = ['<select>'];
+
+    $.each(all_versions, function(version, title) {
+      buf.push('<option value="' + version + '"');
+      if (version == current_version)
+        buf.push(' selected="selected">' + current_release + '</option>');
+      else
+        buf.push('>' + title + '</option>');
+    });
+
+    buf.push('</select>');
+    return buf.join('');
+  }
+
+  function build_language_select(current_language) {
+    var buf = ['<select>'];
+
+    $.each(all_languages, function(language, title) {
+      if (language == current_language)
+        buf.push('<option value="' + language + '" selected="selected">' +
+                 all_languages[current_language] + '</option>');
+      else
+        buf.push('<option value="' + language + '">' + title + '</option>');
+    });
+    buf.push('</select>');
+    return buf.join('');
+  }
+
+  function navigate_to_first_existing(urls) {
+    // Navigate to the first existing URL in urls.
+    var url = urls.shift();
+    if (urls.length == 0) {
+      window.location.href = url;
+      return;
+    }
+    $.ajax({
+      url: url,
+      success: function() {
+        window.location.href = url;
+      },
+      error: function() {
+        navigate_to_first_existing(urls);
+      }
+    });
+  }
+
+  function on_version_switch() {
+    var selected_version = $(this).children('option:selected').attr('value') + '/';
+    var url = window.location.href;
+    var current_language = language_segment_from_url(url);
+    var current_version = version_segment_in_url(url);
+    var new_url = url.replace('.org/' + current_language + current_version,
+                              '.org/' + current_language + selected_version);
+    if (new_url != url) {
+      navigate_to_first_existing([
+        new_url,
+        url.replace('.org/' + current_language + current_version,
+                    '.org/' + selected_version),
+        'https://docs.python.org/' + current_language + selected_version,
+        'https://docs.python.org/' + selected_version,
+        'https://docs.python.org/'
+      ]);
+    }
+  }
+
+  function on_language_switch() {
+    var selected_language = $(this).children('option:selected').attr('value') + '/';
+    var url = window.location.href;
+    var current_language = language_segment_from_url(url);
+    var current_version = version_segment_in_url(url);
+    if (selected_language == 'en/') // Special 'default' case for english.
+      selected_language = '';
+    var new_url = url.replace('.org/' + current_language + current_version,
+                              '.org/' + selected_language + current_version);
+    if (new_url != url) {
+      navigate_to_first_existing([
+        new_url,
+        'https://docs.python.org/'
+      ]);
+    }
+  }
+
+  // Returns the path segment of the language as a string, like 'fr/'
+  // or '' if not found.
+  function language_segment_from_url(url) {
+    var language_regexp = '\.org/([a-z]{2}(?:-[a-z]{2})?/)';
+    var match = url.match(language_regexp);
+    if (match !== null)
+        return match[1];
+    return '';
+  }
+
+  // Returns the path segment of the version as a string, like '3.6/'
+  // or '' if not found.
+  function version_segment_in_url(url) {
+    var language_segment = '(?:[a-z]{2}(?:-[a-z]{2})?/)';
+    var version_segment = '(?:(?:' + version_regexs.join('|') + ')/)';
+    var version_regexp = '\\.org/' + language_segment + '?(' + version_segment + ')';
+    var match = url.match(version_regexp);
+    if (match !== null)
+      return match[1];
+    return ''
+  }
+
+  $(document).ready(function() {
+    var release = DOCUMENTATION_OPTIONS.VERSION;
+    var language_segment = language_segment_from_url(window.location.href);
+    var current_language = language_segment.replace(/\/+$/g, '') || 'en';
+    var version = release.substr(0, 3);
+    var version_select = build_version_select(version, release);
+
+    $('.version_switcher_placeholder').html(version_select);
+    $('.version_switcher_placeholder select').bind('change', on_version_switch);
+
+    var language_select = build_language_select(current_language);
+
+    $('.language_switcher_placeholder').html(language_select);
+    $('.language_switcher_placeholder select').bind('change', on_language_switch);
+  });
+})();
diff --git a/Doc/tools/static/version_switch.js b/Doc/tools/static/version_switch.js
deleted file mode 100644 (file)
index 8b36a61..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-(function() {
-  'use strict';
-
-  var all_versions = {
-    '3.7': 'dev (3.7)',
-    '3.6': '3.6',
-    '3.5': '3.5',
-    '3.4': '3.4',
-    '3.3': '3.3',
-    '2.7': '2.7',
-  };
-
-  function build_select(current_version, current_release) {
-    var buf = ['<select>'];
-
-    $.each(all_versions, function(version, title) {
-      buf.push('<option value="' + version + '"');
-      if (version == current_version)
-        buf.push(' selected="selected">' + current_release + '</option>');
-      else
-        buf.push('>' + title + '</option>');
-    });
-
-    buf.push('</select>');
-    return buf.join('');
-  }
-
-  function patch_url(url, new_version) {
-    var url_re = /\.org\/(\d|py3k|dev|((release\/)?\d\.\d[\w\d\.]*))\//,
-        new_url = url.replace(url_re, '.org/' + new_version + '/');
-
-    if (new_url == url && !new_url.match(url_re)) {
-      // python 2 url without version?
-      new_url = url.replace(/\.org\//, '.org/' + new_version + '/');
-    }
-    return new_url;
-  }
-
-  function on_switch() {
-    var selected = $(this).children('option:selected').attr('value');
-
-    var url = window.location.href,
-        new_url = patch_url(url, selected);
-
-    if (new_url != url) {
-      // check beforehand if url exists, else redirect to version's start page
-      $.ajax({
-        url: new_url,
-        success: function() {
-           window.location.href = new_url;
-        },
-        error: function() {
-           window.location.href = 'https://docs.python.org/' + selected;
-        }
-      });
-    }
-  }
-
-  $(document).ready(function() {
-    var release = DOCUMENTATION_OPTIONS.VERSION;
-    var version = release.substr(0, 3);
-    var select = build_select(version, release);
-
-    $('.version_switcher_placeholder').html(select);
-    $('.version_switcher_placeholder select').bind('change', on_switch);
-  });
-})();
index 2dc5404..6cd5a32 100644 (file)
@@ -325,4 +325,3 @@ whatsnew/3.5,,:warning,'WARNING:root:warning\n'
 whatsnew/3.5,,::,>>> addr6 = ipaddress.IPv6Address('::1')
 whatsnew/3.5,,:root,ERROR:root:exception
 whatsnew/3.5,,:exception,ERROR:root:exception
-whatsnew/changelog,,:version,import sys; I = version[:version.index(' ')]
index 71d0bba..21af621 100644 (file)
@@ -4,7 +4,7 @@
     <ul class="this-page-menu">
       <li><a href="{{ pathto('bugs') }}">{% trans %}Report a Bug{% endtrans %}</a></li>
       <li>
-        <a href="https://github.com/python/cpython/blob/{{ version }}/Doc/{{ sourcename|replace('txt', 'rst') }}"
+        <a href="https://github.com/python/cpython/blob/{{ version }}/Doc/{{ sourcename|replace('.rst.txt', '.rst') }}"
             rel="nofollow">{{ _('Show Source') }}
         </a>
       </li>
index 3a05cb6..d49ebdd 100644 (file)
@@ -18,23 +18,23 @@ in the table are the size of the download files in megabytes.</p>
 <table class="docutils">
   <tr><th>Format</th><th>Packed as .zip</th><th>Packed as .tar.bz2</th></tr>
   <tr><td>PDF (US-Letter paper size)</td>
-    <td><a href="{{ dlbase }}/python-{{ release }}-docs-pdf-letter.zip">Download</a> (ca. 8 MB)</td>
-    <td><a href="{{ dlbase }}/python-{{ release }}-docs-pdf-letter.tar.bz2">Download</a> (ca. 8 MB)</td>
+    <td><a href="{{ dlbase }}/python-{{ release }}-docs-pdf-letter.zip">Download</a> (ca. 13 MB)</td>
+    <td><a href="{{ dlbase }}/python-{{ release }}-docs-pdf-letter.tar.bz2">Download</a> (ca. 13 MB)</td>
   </tr>
   <tr><td>PDF (A4 paper size)</td>
-    <td><a href="{{ dlbase }}/python-{{ release }}-docs-pdf-a4.zip">Download</a> (ca. 8 MB)</td>
-    <td><a href="{{ dlbase }}/python-{{ release }}-docs-pdf-a4.tar.bz2">Download</a> (ca. 8 MB)</td>
+    <td><a href="{{ dlbase }}/python-{{ release }}-docs-pdf-a4.zip">Download</a> (ca. 13 MB)</td>
+    <td><a href="{{ dlbase }}/python-{{ release }}-docs-pdf-a4.tar.bz2">Download</a> (ca. 13 MB)</td>
   </tr>
   <tr><td>HTML</td>
-    <td><a href="{{ dlbase }}/python-{{ release }}-docs-html.zip">Download</a> (ca. 6 MB)</td>
-    <td><a href="{{ dlbase }}/python-{{ release }}-docs-html.tar.bz2">Download</a> (ca. 4 MB)</td>
+    <td><a href="{{ dlbase }}/python-{{ release }}-docs-html.zip">Download</a> (ca. 9 MB)</td>
+    <td><a href="{{ dlbase }}/python-{{ release }}-docs-html.tar.bz2">Download</a> (ca. 6 MB)</td>
   </tr>
   <tr><td>Plain Text</td>
-    <td><a href="{{ dlbase }}/python-{{ release }}-docs-text.zip">Download</a> (ca. 2 MB)</td>
-    <td><a href="{{ dlbase }}/python-{{ release }}-docs-text.tar.bz2">Download</a> (ca. 1.5 MB)</td>
+    <td><a href="{{ dlbase }}/python-{{ release }}-docs-text.zip">Download</a> (ca. 3 MB)</td>
+    <td><a href="{{ dlbase }}/python-{{ release }}-docs-text.tar.bz2">Download</a> (ca. 2 MB)</td>
   </tr>
  <tr><td>EPUB</td>
-    <td><a href="{{ dlbase }}/python-{{ release }}-docs.epub">Download</a> (ca. 4.5 MB)</td>
+    <td><a href="{{ dlbase }}/python-{{ release }}-docs.epub">Download</a> (ca. 5.5 MB)</td>
     <td></td>
   </tr>
 </table>
index 640d8b3..c210667 100644 (file)
@@ -4,7 +4,8 @@
                  style="vertical-align: middle; margin-top: -1px"/></li>
         <li><a href="https://www.python.org/">Python</a>{{ reldelim1 }}</li>
         <li>
-          {%- if versionswitcher is defined %}
+          {%- if switchers is defined %}
+          <span class="language_switcher_placeholder">{{ language or 'en' }}</span>
           <span class="version_switcher_placeholder">{{ release }}</span>
           <a href="{{ pathto('index') }}">{% trans %}Documentation {% endtrans %}</a>{{ reldelim1 }}
           {%- else %}
@@ -41,7 +42,7 @@
     <link rel="canonical" href="https://docs.python.org/3/{{pagename}}.html" />
     {% if builder != "htmlhelp" %}
     {% if not embedded %}<script type="text/javascript" src="{{ pathto('_static/copybutton.js', 1) }}"></script>{% endif %}
-    {% if versionswitcher is defined and not embedded %}<script type="text/javascript" src="{{ pathto('_static/version_switch.js', 1) }}"></script>{% endif %}
+    {% if switchers is defined and not embedded %}<script type="text/javascript" src="{{ pathto('_static/switchers.js', 1) }}"></script>{% endif %}
     {% if pagename == 'whatsnew/changelog' and not embedded %}
     <script type="text/javascript">
       $(document).ready(function() {
index 073444c..95141f9 100644 (file)
@@ -4,6 +4,12 @@
 Classes
 *******
 
+Classes provide a means of bundling data and functionality together.  Creating
+a new class creates a new *type* of object, allowing new *instances* of that
+type to be made.  Each class instance can have attributes attached to it for
+maintaining its state.  Class instances can also have methods (defined by its
+class) for modifying its state.
+
 Compared with other programming languages, Python's class mechanism adds classes
 with a minimum of new syntax and semantics.  It is a mixture of the class
 mechanisms found in C++ and Modula-3.  Python classes provide all the standard
index 54171bc..36b0939 100644 (file)
@@ -157,7 +157,7 @@ Later we will see more functions that return iterables and take iterables as arg
 :keyword:`break` and :keyword:`continue` Statements, and :keyword:`else` Clauses on Loops
 =========================================================================================
 
-The :keyword:`break` statement, like in C, breaks out of the smallest enclosing
+The :keyword:`break` statement, like in C, breaks out of the innermost enclosing
 :keyword:`for` or :keyword:`while` loop.
 
 Loop statements may have an ``else`` clause; it is executed when the loop
index 604688c..8b96ebe 100644 (file)
@@ -67,7 +67,7 @@ Building Python
 If you want to compile CPython yourself, first thing you should do is get the
 `source <https://www.python.org/downloads/source/>`_. You can download either the
 latest release's source or just grab a fresh `clone
-<https://docs.python.org/devguide/setup.html#getting-the-source-code>`_.  (If you want
+<https://devguide.python.org/setup/#getting-the-source-code>`_.  (If you want
 to contribute patches, you will need a clone.)
 
 The build process consists in the usual ::
@@ -140,4 +140,4 @@ Many editors and IDEs provide syntax highlighting, debugging tools, and PEP-8 ch
 
 Please go to `Python Editors <https://wiki.python.org/moin/PythonEditors>`_ and
 `Integrated Development Environments <https://wiki.python.org/moin/IntegratedDevelopmentEnvironments>`_
-for a comprehensive list.
\ No newline at end of file
+for a comprehensive list.
index 68687e9..3d47d7c 100644 (file)
@@ -896,7 +896,7 @@ Compiling Python on Windows
 If you want to compile CPython yourself, first thing you should do is get the
 `source <https://www.python.org/downloads/source/>`_. You can download either the
 latest release's source or just grab a fresh `checkout
-<https://docs.python.org/devguide/setup.html#getting-the-source-code>`_.
+<https://devguide.python.org/setup/#getting-the-source-code>`_.
 
 The source tree contains a build solution and project files for Microsoft
 Visual Studio 2015, which is the compiler used to build the official Python
index cc2fa3d..45b0ab9 100644 (file)
@@ -232,7 +232,7 @@ have adopted Sphinx as their documentation tool.
 
 .. seealso::
 
-   `Documenting Python <https://docs.python.org/devguide/documenting.html>`__
+   `Documenting Python <https://devguide.python.org/documenting/>`__
        Describes how to write for Python's documentation.
 
    `Sphinx <http://sphinx-doc.org/>`__
index 72398f9..6597170 100644 (file)
@@ -1960,7 +1960,7 @@ Other Improvements
   ``.py`` extension.  (Contributed by Paul Moore in :issue:`18569`.)
 
 * A new ``make`` target `coverage-report
-  <https://docs.python.org/devguide/coverage.html#measuring-coverage-of-c-code-with-gcov-and-lcov>`_
+  <https://devguide.python.org/coverage/#measuring-coverage-of-c-code-with-gcov-and-lcov>`_
   will build python, run the test suite, and generate an HTML coverage report
   for the C codebase using ``gcov`` and `lcov
   <http://ltp.sourceforge.net/coverage/lcov.php>`_.
@@ -2176,7 +2176,7 @@ The following obsolete and previously deprecated APIs and features have been
 removed:
 
 * The unmaintained ``Misc/TextMate`` and ``Misc/vim`` directories have been
-  removed (see the `devguide <https://docs.python.org/devguide>`_
+  removed (see the `devguide <https://devguide.python.org>`_
   for suggestions on what to use instead).
 
 * The ``SO`` makefile macro is removed (it was replaced by the
index 1e71bc3..1301f4c 100644 (file)
@@ -45,9 +45,8 @@
    when researching a change.
 
 This article explains the new features in Python 3.6, compared to 3.5.
-Python 3.6 was released on December 23, 2016.  See the
-`changelog <https://docs.python.org/3.6/whatsnew/changelog.html>`_ for a full
-list of changes.
+Python 3.6 was released on December 23, 2016.
+For full details, see the :ref:`changelog <changelog>`.
 
 .. seealso::
 
index 67a12f3..b435614 100644 (file)
@@ -4,4 +4,4 @@
 Changelog
 +++++++++
 
-.. miscnews:: ../../Misc/NEWS
+.. miscnews:: ../build/NEWS
index 7c92524..aacdb22 100644 (file)
@@ -27,8 +27,10 @@ anyone wishing to stay up-to-date after a new release.
    2.1.rst
    2.0.rst
 
-The "Changelog" is a HTML version of the file :source:`Misc/NEWS` which
-contains *all* nontrivial changes to Python for the current version.
+The "Changelog" is an HTML version of the `file built
+<https://pypi.org/project/blurb>`_ from the contents of the
+:source:`Misc/NEWS.d` directory tree, which contains *all* nontrivial changes
+to Python for the current version.
 
 .. toctree::
    :maxdepth: 2
index 59c807e..9058243 100644 (file)
@@ -1,7 +1,7 @@
 # Grammar for Python
 
 # NOTE WELL: You should also follow all the steps listed at
-# https://docs.python.org/devguide/grammar.html
+# https://devguide.python.org/grammar/
 
 # Start symbols for the grammar:
 #       single_input is a single interactive statement;
index 1e48272..38d4709 100644 (file)
@@ -46,6 +46,7 @@ PyAPI_FUNC(int) PyEval_MergeCompilerFlags(PyCompilerFlags *cf);
 #endif
 
 PyAPI_FUNC(int) Py_AddPendingCall(int (*func)(void *), void *arg);
+PyAPI_FUNC(void) _PyEval_SignalReceived(void);
 PyAPI_FUNC(int) Py_MakePendingCalls(void);
 
 /* Protection against deeply nested recursive calls
index 6120e51..1dde17e 100644 (file)
@@ -39,7 +39,7 @@ PyAPI_DATA(PyTypeObject) PyStdPrinter_Type;
 
 /* A routine to check if a file descriptor can be select()-ed. */
 #ifdef HAVE_SELECT
- #define _PyIsSelectable_fd(FD) (((FD) >= 0) && ((FD) < FD_SETSIZE))
+ #define _PyIsSelectable_fd(FD) ((unsigned int)(FD) < (unsigned int)FD_SETSIZE)
 #else
  #define _PyIsSelectable_fd(FD) (1)
 #endif /* HAVE_SELECT */
index 312a6fb..f06b654 100644 (file)
 /*--start constants--*/
 #define PY_MAJOR_VERSION       3
 #define PY_MINOR_VERSION       6
-#define PY_MICRO_VERSION       2
+#define PY_MICRO_VERSION       3
 #define PY_RELEASE_LEVEL       PY_RELEASE_LEVEL_FINAL
 #define PY_RELEASE_SERIAL      0
 
 /* Version as a string */
-#define PY_VERSION             "3.6.2"
+#define PY_VERSION             "3.6.3"
 /*--end constants--*/
 
 /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2.
index 7216a09..6cf69f9 100644 (file)
@@ -187,14 +187,14 @@ PyAPI_FUNC(void) _Py_set_387controlword(unsigned short);
  * result.
  * Caution:
  *    This isn't reliable.  C99 no longer requires libm to set errno under
- *       any exceptional condition, but does require +- HUGE_VAL return
- *       values on overflow.  A 754 box *probably* maps HUGE_VAL to a
- *       double infinity, and we're cool if that's so, unless the input
- *       was an infinity and an infinity is the expected result.  A C89
- *       system sets errno to ERANGE, so we check for that too.  We're
- *       out of luck if a C99 754 box doesn't map HUGE_VAL to +Inf, or
- *       if the returned result is a NaN, or if a C89 box returns HUGE_VAL
- *       in non-overflow cases.
+ *        any exceptional condition, but does require +- HUGE_VAL return
+ *        values on overflow.  A 754 box *probably* maps HUGE_VAL to a
+ *        double infinity, and we're cool if that's so, unless the input
+ *        was an infinity and an infinity is the expected result.  A C89
+ *        system sets errno to ERANGE, so we check for that too.  We're
+ *        out of luck if a C99 754 box doesn't map HUGE_VAL to +Inf, or
+ *        if the returned result is a NaN, or if a C89 box returns HUGE_VAL
+ *        in non-overflow cases.
  *    X is evaluated more than once.
  * Some platforms have better way to spell this, so expect some #ifdef'ery.
  *
@@ -211,8 +211,20 @@ PyAPI_FUNC(void) _Py_set_387controlword(unsigned short);
 #define Py_OVERFLOWED(X) isinf(X)
 #else
 #define Py_OVERFLOWED(X) ((X) != 0.0 && (errno == ERANGE ||    \
-                                        (X) == Py_HUGE_VAL || \
-                                        (X) == -Py_HUGE_VAL))
-#endif
+                                         (X) == Py_HUGE_VAL || \
+                                         (X) == -Py_HUGE_VAL))
+#endif
+
+/* Return whether integral type *type* is signed or not. */
+#define _Py_IntegralTypeSigned(type) ((type)(-1) < 0)
+/* Return the maximum value of integral type *type*. */
+#define _Py_IntegralTypeMax(type) ((_Py_IntegralTypeSigned(type)) ? (((((type)1 << (sizeof(type)*CHAR_BIT - 2)) - 1) << 1) + 1) : ~(type)0)
+/* Return the minimum value of integral type *type*. */
+#define _Py_IntegralTypeMin(type) ((_Py_IntegralTypeSigned(type)) ? -_Py_IntegralTypeMax(type) - 1 : 0)
+/* Check whether *v* is in the range of integral type *type*. This is most
+ * useful if *v* is floating-point, since demoting a floating-point *v* to an
+ * integral type that cannot represent *v*'s integral part is undefined
+ * behavior. */
+#define _Py_InIntegralTypeRange(type, v) (_Py_IntegralTypeMin(type) <= v && v <= _Py_IntegralTypeMax(type))
 
 #endif /* Py_PYMATH_H */
index 5b87718..f498873 100644 (file)
@@ -752,23 +752,27 @@ PyAPI_FUNC(Py_UCS4*) PyUnicode_AsUCS4(
 PyAPI_FUNC(Py_UCS4*) PyUnicode_AsUCS4Copy(PyObject *unicode);
 #endif
 
+#ifndef Py_LIMITED_API
 /* Return a read-only pointer to the Unicode object's internal
    Py_UNICODE buffer.
    If the wchar_t/Py_UNICODE representation is not yet available, this
    function will calculate it. */
 
-#ifndef Py_LIMITED_API
 PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
     PyObject *unicode           /* Unicode object */
     );
-#endif
+
+/* Similar to PyUnicode_AsUnicode(), but raises a ValueError if the string
+   contains null characters. */
+PyAPI_FUNC(const Py_UNICODE *) _PyUnicode_AsUnicode(
+    PyObject *unicode           /* Unicode object */
+    );
 
 /* Return a read-only pointer to the Unicode object's internal
    Py_UNICODE buffer and save the length at size.
    If the wchar_t/Py_UNICODE representation is not yet available, this
    function will calculate it. */
 
-#ifndef Py_LIMITED_API
 PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicodeAndSize(
     PyObject *unicode,          /* Unicode object */
     Py_ssize_t *size            /* location where to save the length */
@@ -1063,6 +1067,12 @@ PyAPI_FUNC(wchar_t*) PyUnicode_AsWideCharString(
     );
 
 #ifndef Py_LIMITED_API
+/* Similar to PyUnicode_AsWideCharString(unicode, NULL), but check if
+   the string contains null characters. */
+PyAPI_FUNC(wchar_t*) _PyUnicode_AsWideCharString(
+    PyObject *unicode           /* Unicode object */
+    );
+
 PyAPI_FUNC(void*) _PyUnicode_AsKind(PyObject *s, unsigned int kind);
 #endif
 
diff --git a/LICENSE b/LICENSE
index f5d0b39..529349e 100644 (file)
--- a/LICENSE
+++ b/LICENSE
@@ -13,12 +13,11 @@ software.
 
 In May 2000, Guido and the Python core development team moved to
 BeOpen.com to form the BeOpen PythonLabs team.  In October of the same
-year, the PythonLabs team moved to Digital Creations (now Zope
-Corporation, see http://www.zope.com).  In 2001, the Python Software
-Foundation (PSF, see http://www.python.org/psf/) was formed, a
-non-profit organization created specifically to own Python-related
-Intellectual Property.  Zope Corporation is a sponsoring member of
-the PSF.
+year, the PythonLabs team moved to Digital Creations, which became
+Zope Corporation.  In 2001, the Python Software Foundation (PSF, see
+https://www.python.org/psf/) was formed, a non-profit organization
+created specifically to own Python-related Intellectual Property.
+Zope Corporation was a sponsoring member of the PSF.
 
 All Python releases are Open Source (see http://www.opensource.org for
 the Open Source Definition).  Historically, most, but not all, Python
index 08e9441..b2adaad 100644 (file)
@@ -197,7 +197,7 @@ def coroutine(func):
     """
     if _inspect_iscoroutinefunction(func):
         # In Python 3.5 that's all we need to do for coroutines
-        # defiend with "async def".
+        # defined with "async def".
         # Wrapping in CoroWrapper will happen via
         # 'sys.set_coroutine_wrapper' function.
         return func
index e85634e..d41f3f5 100644 (file)
@@ -611,8 +611,7 @@ _lock = threading.Lock()
 
 # A TLS for the running event loop, used by _get_running_loop.
 class _RunningLoop(threading.local):
-    _loop = None
-    _pid = None
+    loop_pid = (None, None)
 
 
 _running_loop = _RunningLoop()
@@ -624,8 +623,8 @@ def _get_running_loop():
     This is a low-level function intended to be used by event loops.
     This function is thread-specific.
     """
-    running_loop = _running_loop._loop
-    if running_loop is not None and _running_loop._pid == os.getpid():
+    running_loop, pid = _running_loop.loop_pid
+    if running_loop is not None and pid == os.getpid():
         return running_loop
 
 
@@ -635,8 +634,7 @@ def _set_running_loop(loop):
     This is a low-level function intended to be used by event loops.
     This function is thread-specific.
     """
-    _running_loop._pid = os.getpid()
-    _running_loop._loop = loop
+    _running_loop.loop_pid = (loop, os.getpid())
 
 
 def _init_event_loop_policy():
index b12d5db..8b8c22a 100644 (file)
@@ -33,6 +33,7 @@ from . import selectors
 from . import tasks
 from .coroutines import coroutine
 from .log import logger
+from test import support
 
 
 if sys.platform == 'win32':  # pragma: no cover
@@ -437,12 +438,19 @@ def get_function_source(func):
 
 
 class TestCase(unittest.TestCase):
+    @staticmethod
+    def close_loop(loop):
+        executor = loop._default_executor
+        if executor is not None:
+            executor.shutdown(wait=True)
+        loop.close()
+
     def set_event_loop(self, loop, *, cleanup=True):
         assert loop is not None
         # ensure that the event loop is passed explicitly in asyncio
         events.set_event_loop(None)
         if cleanup:
-            self.addCleanup(loop.close)
+            self.addCleanup(self.close_loop, loop)
 
     def new_test_loop(self, gen=None):
         loop = TestLoop(gen)
@@ -455,6 +463,7 @@ class TestCase(unittest.TestCase):
     def setUp(self):
         self._get_running_loop = events._get_running_loop
         events._get_running_loop = lambda: None
+        self._thread_cleanup = support.threading_setup()
 
     def tearDown(self):
         self.unpatch_get_running_loop()
@@ -465,6 +474,10 @@ class TestCase(unittest.TestCase):
         # in an except block of a generator
         self.assertEqual(sys.exc_info(), (None, None, None))
 
+        self.doCleanups()
+        support.threading_cleanup(*self._thread_cleanup)
+        support.reap_children()
+
     if not compat.PY34:
         # Python 3.3 compatibility
         def subTest(self, *args, **kwargs):
index 705e406..03d1683 100644 (file)
@@ -619,8 +619,9 @@ if os.name == 'posix':
         def close(self):
             if self.fd < 0:
                 return
-            os.close(self.fd)
+            fd = self.fd
             self.fd = -1
+            os.close(fd)
 
         def fileno(self):
             return self.fd
index 85b4c3c..26aeac1 100644 (file)
@@ -85,9 +85,7 @@ class OrderedDict(dict):
 
     def __init__(*args, **kwds):
         '''Initialize an ordered dictionary.  The signature is the same as
-        regular dictionaries, but keyword arguments are not recommended because
-        their insertion order is arbitrary.
-
+        regular dictionaries.  Keyword argument order is preserved.
         '''
         if not args:
             raise TypeError("descriptor '__init__' of 'OrderedDict' object "
@@ -157,9 +155,9 @@ class OrderedDict(dict):
         dict.clear(self)
 
     def popitem(self, last=True):
-        '''od.popitem() -> (k, v), return and remove a (key, value) pair.
-        Pairs are returned in LIFO order if last is true or FIFO order if false.
+        '''Remove and return a (key, value) pair from the dictionary.
 
+        Pairs are returned in LIFO order if last is true or FIFO order if false.
         '''
         if not self:
             raise KeyError('dictionary is empty')
index 295489c..6bace6c 100644 (file)
@@ -170,6 +170,29 @@ def _create_and_install_waiters(fs, return_when):
 
     return waiter
 
+
+def _yield_finished_futures(fs, waiter, ref_collect):
+    """
+    Iterate on the list *fs*, yielding finished futures one by one in
+    reverse order.
+    Before yielding a future, *waiter* is removed from its waiters
+    and the future is removed from each set in the collection of sets
+    *ref_collect*.
+
+    The aim of this function is to avoid keeping stale references after
+    the future is yielded and before the iterator resumes.
+    """
+    while fs:
+        f = fs[-1]
+        for futures_set in ref_collect:
+            futures_set.remove(f)
+        with f._condition:
+            f._waiters.remove(waiter)
+        del f
+        # Careful not to keep a reference to the popped value
+        yield fs.pop()
+
+
 def as_completed(fs, timeout=None):
     """An iterator over the given futures that yields each as it completes.
 
@@ -192,15 +215,17 @@ def as_completed(fs, timeout=None):
         end_time = timeout + time.time()
 
     fs = set(fs)
+    total_futures = len(fs)
     with _AcquireFutures(fs):
         finished = set(
                 f for f in fs
                 if f._state in [CANCELLED_AND_NOTIFIED, FINISHED])
         pending = fs - finished
         waiter = _create_and_install_waiters(fs, _AS_COMPLETED)
-
+    finished = list(finished)
     try:
-        yield from finished
+        yield from _yield_finished_futures(finished, waiter,
+                                           ref_collect=(fs,))
 
         while pending:
             if timeout is None:
@@ -210,7 +235,7 @@ def as_completed(fs, timeout=None):
                 if wait_timeout < 0:
                     raise TimeoutError(
                             '%d (of %d) futures unfinished' % (
-                            len(pending), len(fs)))
+                            len(pending), total_futures))
 
             waiter.event.wait(wait_timeout)
 
@@ -219,11 +244,13 @@ def as_completed(fs, timeout=None):
                 waiter.finished_futures = []
                 waiter.event.clear()
 
-            for future in finished:
-                yield future
-                pending.remove(future)
+            # reverse to keep finishing order
+            finished.reverse()
+            yield from _yield_finished_futures(finished, waiter,
+                                               ref_collect=(fs, pending))
 
     finally:
+        # Remove waiter from unfinished futures
         for f in fs:
             with f._condition:
                 f._waiters.remove(waiter)
@@ -551,11 +578,14 @@ class Executor(object):
         # before the first iterator value is required.
         def result_iterator():
             try:
-                for future in fs:
+                # reverse to keep finishing order
+                fs.reverse()
+                while fs:
+                    # Careful not to keep a reference to the popped future
                     if timeout is None:
-                        yield future.result()
+                        yield fs.pop().result()
                     else:
-                        yield future.result(end_time - time.time())
+                        yield fs.pop().result(end_time - time.time())
             finally:
                 for future in fs:
                     future.cancel()
index 8f1d714..03b28ab 100644 (file)
@@ -357,6 +357,18 @@ def _check_system_limits():
     raise NotImplementedError(_system_limited)
 
 
+def _chain_from_iterable_of_lists(iterable):
+    """
+    Specialized implementation of itertools.chain.from_iterable.
+    Each item in *iterable* should be a list.  This function is
+    careful not to keep references to yielded objects.
+    """
+    for element in iterable:
+        element.reverse()
+        while element:
+            yield element.pop()
+
+
 class BrokenProcessPool(RuntimeError):
     """
     Raised when a process in a ProcessPoolExecutor terminated abruptly
@@ -482,7 +494,7 @@ class ProcessPoolExecutor(_base.Executor):
         results = super().map(partial(_process_chunk, fn),
                               _get_chunks(*iterables, chunksize=chunksize),
                               timeout=timeout)
-        return itertools.chain.from_iterable(results)
+        return _chain_from_iterable_of_lists(results)
 
     def shutdown(self, wait=True):
         with self._shutdown_lock:
index 03d276b..0b5d537 100644 (file)
@@ -7,6 +7,7 @@ __author__ = 'Brian Quinlan (brian@sweetapp.com)'
 
 import atexit
 from concurrent.futures import _base
+import itertools
 import queue
 import threading
 import weakref
@@ -53,8 +54,10 @@ class _WorkItem(object):
 
         try:
             result = self.fn(*self.args, **self.kwargs)
-        except BaseException as e:
-            self.future.set_exception(e)
+        except BaseException as exc:
+            self.future.set_exception(exc)
+            # Break a reference cycle with the exception 'exc'
+            self = None
         else:
             self.future.set_result(result)
 
@@ -81,6 +84,10 @@ def _worker(executor_reference, work_queue):
         _base.LOGGER.critical('Exception in worker', exc_info=True)
 
 class ThreadPoolExecutor(_base.Executor):
+
+    # Used to assign unique thread names when thread_name_prefix is not supplied.
+    _counter = itertools.count().__next__
+
     def __init__(self, max_workers=None, thread_name_prefix=''):
         """Initializes a new ThreadPoolExecutor instance.
 
@@ -101,7 +108,8 @@ class ThreadPoolExecutor(_base.Executor):
         self._threads = set()
         self._shutdown = False
         self._shutdown_lock = threading.Lock()
-        self._thread_name_prefix = thread_name_prefix
+        self._thread_name_prefix = (thread_name_prefix or
+                                    ("ThreadPoolExecutor-%d" % self._counter()))
 
     def submit(self, fn, *args, **kwargs):
         with self._shutdown_lock:
index 67f5bb0..cef9984 100644 (file)
@@ -128,7 +128,11 @@ def _slotnames(cls):
                         continue
                     # mangled names
                     elif name.startswith('__') and not name.endswith('__'):
-                        names.append('_%s%s' % (c.__name__, name))
+                        stripped = c.__name__.lstrip('_')
+                        if stripped:
+                            names.append('_%s%s' % (stripped, name))
+                        else:
+                            names.append(name)
                     else:
                         names.append(name)
 
index 45571f3..f3b65b9 100644 (file)
@@ -62,6 +62,8 @@ class LoaderTest(unittest.TestCase):
             windll["kernel32"].GetModuleHandleW
             windll.LoadLibrary("kernel32").GetModuleHandleW
             WinDLL("kernel32").GetModuleHandleW
+            # embedded null character
+            self.assertRaises(ValueError, windll.LoadLibrary, "kernel32\0")
 
     @unittest.skipUnless(os.name == "nt",
                          'test specific to Windows')
index d68397e..f3c0e23 100644 (file)
@@ -112,6 +112,34 @@ Complete._fields_ = [("a", c_long)]
 # This table contains format strings as they look on little endian
 # machines.  The test replaces '<' with '>' on big endian machines.
 #
+
+# Platform-specific type codes
+s_bool = {1: '?', 2: 'H', 4: 'L', 8: 'Q'}[sizeof(c_bool)]
+s_short = {2: 'h', 4: 'l', 8: 'q'}[sizeof(c_short)]
+s_ushort = {2: 'H', 4: 'L', 8: 'Q'}[sizeof(c_ushort)]
+s_int = {2: 'h', 4: 'i', 8: 'q'}[sizeof(c_int)]
+s_uint = {2: 'H', 4: 'I', 8: 'Q'}[sizeof(c_uint)]
+s_long = {4: 'l', 8: 'q'}[sizeof(c_long)]
+s_ulong = {4: 'L', 8: 'Q'}[sizeof(c_ulong)]
+s_longlong = "q"
+s_ulonglong = "Q"
+s_float = "f"
+s_double = "d"
+s_longdouble = "g"
+
+# Alias definitions in ctypes/__init__.py
+if c_int is c_long:
+    s_int = s_long
+if c_uint is c_ulong:
+    s_uint = s_ulong
+if c_longlong is c_long:
+    s_longlong = s_long
+if c_ulonglong is c_ulong:
+    s_ulonglong = s_ulong
+if c_longdouble is c_double:
+    s_longdouble = s_double
+
+
 native_types = [
     # type                      format                  shape           calc itemsize
 
@@ -120,52 +148,51 @@ native_types = [
     (c_char,                    "<c",                   (),           c_char),
     (c_byte,                    "<b",                   (),           c_byte),
     (c_ubyte,                   "<B",                   (),           c_ubyte),
-    (c_short,                   "<h",                   (),           c_short),
-    (c_ushort,                  "<H",                   (),           c_ushort),
+    (c_short,                   "<" + s_short,          (),           c_short),
+    (c_ushort,                  "<" + s_ushort,         (),           c_ushort),
 
-    # c_int and c_uint may be aliases to c_long
-    #(c_int,                     "<i",                   (),           c_int),
-    #(c_uint,                    "<I",                   (),           c_uint),
+    (c_int,                     "<" + s_int,            (),           c_int),
+    (c_uint,                    "<" + s_uint,           (),           c_uint),
 
-    (c_long,                    "<l",                   (),           c_long),
-    (c_ulong,                   "<L",                   (),           c_ulong),
+    (c_long,                    "<" + s_long,           (),           c_long),
+    (c_ulong,                   "<" + s_ulong,          (),           c_ulong),
 
-    # c_longlong and c_ulonglong are aliases on 64-bit platforms
-    #(c_longlong,                "<q",                   None,           c_longlong),
-    #(c_ulonglong,               "<Q",                   None,           c_ulonglong),
+    (c_longlong,                "<" + s_longlong,       (),           c_longlong),
+    (c_ulonglong,               "<" + s_ulonglong,      (),           c_ulonglong),
 
     (c_float,                   "<f",                   (),           c_float),
     (c_double,                  "<d",                   (),           c_double),
-    # c_longdouble may be an alias to c_double
 
-    (c_bool,                    "<?",                   (),           c_bool),
+    (c_longdouble,              "<" + s_longdouble,     (),           c_longdouble),
+
+    (c_bool,                    "<" + s_bool,           (),           c_bool),
     (py_object,                 "<O",                   (),           py_object),
 
     ## pointers
 
     (POINTER(c_byte),           "&<b",                  (),           POINTER(c_byte)),
-    (POINTER(POINTER(c_long)),  "&&<l",                 (),           POINTER(POINTER(c_long))),
+    (POINTER(POINTER(c_long)),  "&&<" + s_long,         (),           POINTER(POINTER(c_long))),
 
     ## arrays and pointers
 
     (c_double * 4,              "<d",                   (4,),           c_double),
     (c_float * 4 * 3 * 2,       "<f",                   (2,3,4),        c_float),
-    (POINTER(c_short) * 2,      "&<h",                  (2,),           POINTER(c_short)),
-    (POINTER(c_short) * 2 * 3,  "&<h",                  (3,2,),         POINTER(c_short)),
-    (POINTER(c_short * 2),      "&(2)<h",               (),           POINTER(c_short)),
+    (POINTER(c_short) * 2,      "&<" + s_short,         (2,),           POINTER(c_short)),
+    (POINTER(c_short) * 2 * 3,  "&<" + s_short,         (3,2,),         POINTER(c_short)),
+    (POINTER(c_short * 2),      "&(2)<" + s_short,      (),             POINTER(c_short)),
 
     ## structures and unions
 
-    (Point,                     "T{<l:x:<l:y:}",        (),           Point),
+    (Point,                     "T{<l:x:<l:y:}".replace('l', s_long),  (),  Point),
     # packed structures do not implement the pep
-    (PackedPoint,               "B",                    (),           PackedPoint),
-    (Point2,                    "T{<l:x:<l:y:}",        (),           Point2),
-    (EmptyStruct,               "T{}",                  (),           EmptyStruct),
+    (PackedPoint,               "B",                                   (),  PackedPoint),
+    (Point2,                    "T{<l:x:<l:y:}".replace('l', s_long),  (),  Point2),
+    (EmptyStruct,               "T{}",                                 (),  EmptyStruct),
     # the pep does't support unions
-    (aUnion,                    "B",                    (),           aUnion),
+    (aUnion,                    "B",                                   (),  aUnion),
     # structure with sub-arrays
-    (StructWithArrays,          "T{(2,3)<l:x:(4)T{<l:x:<l:y:}:y:}", (),  StructWithArrays),
-    (StructWithArrays * 3,      "T{(2,3)<l:x:(4)T{<l:x:<l:y:}:y:}", (3,),  StructWithArrays),
+    (StructWithArrays, "T{(2,3)<l:x:(4)T{<l:x:<l:y:}:y:}".replace('l', s_long), (), StructWithArrays),
+    (StructWithArrays * 3, "T{(2,3)<l:x:(4)T{<l:x:<l:y:}:y:}".replace('l', s_long), (3,), StructWithArrays),
 
     ## pointer to incomplete structure
     (Incomplete,                "B",                    (),           Incomplete),
@@ -173,7 +200,7 @@ native_types = [
 
     # 'Complete' is a structure that starts incomplete, but is completed after the
     # pointer type to it has been created.
-    (Complete,                  "T{<l:a:}",             (),           Complete),
+    (Complete,                  "T{<l:a:}".replace('l', s_long), (), Complete),
     # Unfortunately the pointer format string is not fixed...
     (POINTER(Complete),         "&B",                   (),           POINTER(Complete)),
 
@@ -196,10 +223,10 @@ class LEPoint(LittleEndianStructure):
 # and little endian machines.
 #
 endian_types = [
-    (BEPoint,                   "T{>l:x:>l:y:}",        (),           BEPoint),
-    (LEPoint,                   "T{<l:x:<l:y:}",        (),           LEPoint),
-    (POINTER(BEPoint),          "&T{>l:x:>l:y:}",       (),           POINTER(BEPoint)),
-    (POINTER(LEPoint),          "&T{<l:x:<l:y:}",       (),           POINTER(LEPoint)),
+    (BEPoint, "T{>l:x:>l:y:}".replace('l', s_long), (), BEPoint),
+    (LEPoint, "T{<l:x:<l:y:}".replace('l', s_long), (), LEPoint),
+    (POINTER(BEPoint), "&T{>l:x:>l:y:}".replace('l', s_long), (), POINTER(BEPoint)),
+    (POINTER(LEPoint), "&T{<l:x:<l:y:}".replace('l', s_long), (), POINTER(LEPoint)),
     ]
 
 if __name__ == "__main__":
index 240dc0c..a3932f1 100644 (file)
@@ -134,7 +134,7 @@ class SlicesTestCase(unittest.TestCase):
         dll.my_wcsdup.restype = POINTER(c_wchar)
         dll.my_wcsdup.argtypes = POINTER(c_wchar),
         dll.my_free.restype = None
-        res = dll.my_wcsdup(s)
+        res = dll.my_wcsdup(s[:-1])
         self.assertEqual(res[:len(s)], s)
         self.assertEqual(res[:len(s):], s)
         self.assertEqual(res[len(s)-1:-1:-1], s[::-1])
@@ -153,7 +153,7 @@ class SlicesTestCase(unittest.TestCase):
             dll.my_wcsdup.restype = POINTER(c_long)
         else:
             self.skipTest('Pointers to c_wchar are not supported')
-        res = dll.my_wcsdup(s)
+        res = dll.my_wcsdup(s[:-1])
         tmpl = list(range(ord("a"), ord("z")+1))
         self.assertEqual(res[:len(s)-1], tmpl)
         self.assertEqual(res[:len(s)-1:], tmpl)
index 5d5579c..b95536f 100644 (file)
@@ -2271,7 +2271,8 @@ else:
          _check_tzinfo_arg, _check_tzname, _check_utc_offset, _cmp, _cmperror,
          _date_class, _days_before_month, _days_before_year, _days_in_month,
          _format_time, _is_leap, _isoweek1monday, _math, _ord2ymd,
-         _time, _time_class, _tzinfo_class, _wrap_strftime, _ymd2ord)
+         _time, _time_class, _tzinfo_class, _wrap_strftime, _ymd2ord,
+         _divide_and_round)
     # XXX Since import * above excludes names that start with _,
     # docstring does not get overwritten. In the future, it may be
     # appropriate to maintain a single module level docstring and
index b120273..ef1356b 100644 (file)
@@ -17,6 +17,7 @@ import os
 import shutil
 import stat
 import subprocess
+import winreg
 
 from distutils.errors import DistutilsExecError, DistutilsPlatformError, \
                              CompileError, LibError, LinkError
@@ -24,10 +25,9 @@ from distutils.ccompiler import CCompiler, gen_lib_options
 from distutils import log
 from distutils.util import get_platform
 
-import winreg
 from itertools import count
 
-def _find_vcvarsall(plat_spec):
+def _find_vc2015():
     try:
         key = winreg.OpenKeyEx(
             winreg.HKEY_LOCAL_MACHINE,
@@ -38,9 +38,9 @@ def _find_vcvarsall(plat_spec):
         log.debug("Visual C++ is not registered")
         return None, None
 
+    best_version = 0
+    best_dir = None
     with key:
-        best_version = 0
-        best_dir = None
         for i in count():
             try:
                 v, vc_dir, vt = winreg.EnumValue(key, i)
@@ -53,25 +53,74 @@ def _find_vcvarsall(plat_spec):
                     continue
                 if version >= 14 and version > best_version:
                     best_version, best_dir = version, vc_dir
-        if not best_version:
-            log.debug("No suitable Visual C++ version found")
-            return None, None
+    return best_version, best_dir
+
+def _find_vc2017():
+    import _findvs
+    import threading
+
+    best_version = 0,   # tuple for full version comparisons
+    best_dir = None
+
+    # We need to call findall() on its own thread because it will
+    # initialize COM.
+    all_packages = []
+    def _getall():
+        all_packages.extend(_findvs.findall())
+    t = threading.Thread(target=_getall)
+    t.start()
+    t.join()
+
+    for name, version_str, path, packages in all_packages:
+        if 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64' in packages:
+            vc_dir = os.path.join(path, 'VC', 'Auxiliary', 'Build')
+            if not os.path.isdir(vc_dir):
+                continue
+            try:
+                version = tuple(int(i) for i in version_str.split('.'))
+            except (ValueError, TypeError):
+                continue
+            if version > best_version:
+                best_version, best_dir = version, vc_dir
+    try:
+        best_version = best_version[0]
+    except IndexError:
+        best_version = None
+    return best_version, best_dir
 
-        vcvarsall = os.path.join(best_dir, "vcvarsall.bat")
-        if not os.path.isfile(vcvarsall):
-            log.debug("%s cannot be found", vcvarsall)
-            return None, None
+def _find_vcvarsall(plat_spec):
+    best_version, best_dir = _find_vc2017()
+    vcruntime = None
+    vcruntime_plat = 'x64' if 'amd64' in plat_spec else 'x86'
+    if best_version:
+        vcredist = os.path.join(best_dir, "..", "..", "redist", "MSVC", "**",
+            "Microsoft.VC141.CRT", "vcruntime140.dll")
+        try:
+            import glob
+            vcruntime = glob.glob(vcredist, recursive=True)[-1]
+        except (ImportError, OSError, LookupError):
+            vcruntime = None
+
+    if not best_version:
+        best_version, best_dir = _find_vc2015()
+        if best_version:
+            vcruntime = os.path.join(best_dir, 'redist', vcruntime_plat,
+                "Microsoft.VC140.CRT", "vcruntime140.dll")
+
+    if not best_version:
+        log.debug("No suitable Visual C++ version found")
+        return None, None
 
+    vcvarsall = os.path.join(best_dir, "vcvarsall.bat")
+    if not os.path.isfile(vcvarsall):
+        log.debug("%s cannot be found", vcvarsall)
+        return None, None
+
+    if not vcruntime or not os.path.isfile(vcruntime):
+        log.debug("%s cannot be found", vcruntime)
         vcruntime = None
-        vcruntime_spec = _VCVARS_PLAT_TO_VCRUNTIME_REDIST.get(plat_spec)
-        if vcruntime_spec:
-            vcruntime = os.path.join(best_dir,
-                vcruntime_spec.format(best_version))
-            if not os.path.isfile(vcruntime):
-                log.debug("%s cannot be found", vcruntime)
-                vcruntime = None
 
-        return vcvarsall, vcruntime
+    return vcvarsall, vcruntime
 
 def _get_vc_env(plat_spec):
     if os.getenv("DISTUTILS_USE_SDK"):
@@ -130,14 +179,6 @@ PLAT_TO_VCVARS = {
     'win-amd64' : 'x86_amd64',
 }
 
-# A map keyed by get_platform() return values to the file under
-# the VC install directory containing the vcruntime redistributable.
-_VCVARS_PLAT_TO_VCRUNTIME_REDIST = {
-    'x86' : 'redist\\x86\\Microsoft.VC{0}0.CRT\\vcruntime{0}0.dll',
-    'amd64' : 'redist\\x64\\Microsoft.VC{0}0.CRT\\vcruntime{0}0.dll',
-    'x86_amd64' : 'redist\\x64\\Microsoft.VC{0}0.CRT\\vcruntime{0}0.dll',
-}
-
 # A set containing the DLLs that are guaranteed to be available for
 # all micro versions of this Python version. Known extension
 # dependencies that are not in this set will be copied to the output
index d3e1d3a..6309c3e 100644 (file)
@@ -318,26 +318,30 @@ class bdist_wininst(Command):
         # string compares seem wrong, but are what sysconfig.py itself uses
         if self.target_version and self.target_version < cur_version:
             if self.target_version < "2.4":
-                bv = 6.0
+                bv = '6.0'
             elif self.target_version == "2.4":
-                bv = 7.1
+                bv = '7.1'
             elif self.target_version == "2.5":
-                bv = 8.0
+                bv = '8.0'
             elif self.target_version <= "3.2":
-                bv = 9.0
+                bv = '9.0'
             elif self.target_version <= "3.4":
-                bv = 10.0
+                bv = '10.0'
             else:
-                bv = 14.0
+                bv = '14.0'
         else:
             # for current version - use authoritative check.
             try:
                 from msvcrt import CRT_ASSEMBLY_VERSION
             except ImportError:
                 # cross-building, so assume the latest version
-                bv = 14.0
+                bv = '14.0'
             else:
-                bv = float('.'.join(CRT_ASSEMBLY_VERSION.split('.', 2)[:2]))
+                bv = '.'.join(CRT_ASSEMBLY_VERSION.split('.', 2)[:2])
+                if bv == '14.11':
+                    # v141 and v140 are binary compatible,
+                    # so keep using the 14.0 stub.
+                    bv = '14.0'
 
 
         # wininst-x.y.exe is in the same directory as this file
@@ -353,7 +357,7 @@ class bdist_wininst(Command):
         else:
             sfix = ''
 
-        filename = os.path.join(directory, "wininst-%.1f%s.exe" % (bv, sfix))
+        filename = os.path.join(directory, "wininst-%s%s.exe" % (bv, sfix))
         f = open(filename, "rb")
         try:
             return f.read()
index 4dc2488..70a9c93 100644 (file)
@@ -76,12 +76,12 @@ class msvccompilerTestCase(support.TempdirManager,
         compiler = _msvccompiler.MSVCCompiler()
         compiler.initialize()
         dll = compiler._vcruntime_redist
-        self.assertTrue(os.path.isfile(dll))
+        self.assertTrue(os.path.isfile(dll), dll or "<None>")
 
         compiler._copy_vcruntime(tempdir)
 
         self.assertFalse(os.path.isfile(os.path.join(
-            tempdir, os.path.basename(dll))))
+            tempdir, os.path.basename(dll))), dll or "<None>")
 
     def test_get_vc_env_unicode(self):
         import distutils._msvccompiler as _msvccompiler
@@ -101,6 +101,30 @@ class msvccompilerTestCase(support.TempdirManager,
             if old_distutils_use_sdk:
                 os.environ['DISTUTILS_USE_SDK'] = old_distutils_use_sdk
 
+    def test_get_vc2017(self):
+        import distutils._msvccompiler as _msvccompiler
+
+        # This function cannot be mocked, so pass it if we find VS 2017
+        # and mark it skipped if we do not.
+        version, path = _msvccompiler._find_vc2017()
+        if version:
+            self.assertGreaterEqual(version, 15)
+            self.assertTrue(os.path.isdir(path))
+        else:
+            raise unittest.SkipTest("VS 2017 is not installed")
+
+    def test_get_vc2015(self):
+        import distutils._msvccompiler as _msvccompiler
+
+        # This function cannot be mocked, so pass it if we find VS 2015
+        # and mark it skipped if we do not.
+        version, path = _msvccompiler._find_vc2015()
+        if version:
+            self.assertGreaterEqual(version, 14)
+            self.assertTrue(os.path.isdir(path))
+        else:
+            raise unittest.SkipTest("VS 2015 is not installed")
+
 def test_suite():
     return unittest.makeSuite(msvccompilerTestCase)
 
index 57d01fb..9b9697f 100644 (file)
@@ -341,9 +341,7 @@ class TokenList(list):
             # avoid infinite recursion.
             ws = part.pop_leading_fws()
             if ws is not None:
-                # Peel off the leading whitespace and make it sticky, to
-                # avoid infinite recursion.
-                folded.stickyspace = str(part.pop(0))
+                folded.stickyspace = str(ws)
                 if folded.append_if_fits(part):
                     continue
             if part.has_fws:
index 056400d..73dd613 100644 (file)
@@ -381,7 +381,7 @@ class EnumMeta(type):
         # special processing needed for names?
         if isinstance(names, str):
             names = names.replace(',', ' ').split()
-        if isinstance(names, (tuple, list)) and isinstance(names[0], str):
+        if isinstance(names, (tuple, list)) and names and isinstance(names[0], str):
             original_names, names = names, []
             last_values = []
             for count, name in enumerate(original_names):
index 8f36f53..a02e595 100644 (file)
@@ -186,6 +186,8 @@ class FTP:
 
     # Internal: send one line to the server, appending CRLF
     def putline(self, line):
+        if '\r' in line or '\n' in line:
+            raise ValueError('an illegal newline character should not be contained')
         line = line + CRLF
         if self.debugging > 1:
             print('*put*', self.sanitize(line))
index 57d2c74..6b215af 100644 (file)
@@ -270,7 +270,9 @@ class NullTranslations:
     def lgettext(self, message):
         if self._fallback:
             return self._fallback.lgettext(message)
-        return message
+        if self._output_charset:
+            return message.encode(self._output_charset)
+        return message.encode(locale.getpreferredencoding())
 
     def ngettext(self, msgid1, msgid2, n):
         if self._fallback:
@@ -284,9 +286,12 @@ class NullTranslations:
         if self._fallback:
             return self._fallback.lngettext(msgid1, msgid2, n)
         if n == 1:
-            return msgid1
+            tmsg = msgid1
         else:
-            return msgid2
+            tmsg = msgid2
+        if self._output_charset:
+            return tmsg.encode(self._output_charset)
+        return tmsg.encode(locale.getpreferredencoding())
 
     def info(self):
         return self._info
@@ -368,7 +373,7 @@ class GNUTranslations(NullTranslations):
             if mlen == 0:
                 # Catalog description
                 lastk = None
-                for b_item in tmsg.split('\n'.encode("ascii")):
+                for b_item in tmsg.split(b'\n'):
                     item = b_item.decode().strip()
                     if not item:
                         continue
@@ -416,7 +421,7 @@ class GNUTranslations(NullTranslations):
         if tmsg is missing:
             if self._fallback:
                 return self._fallback.lgettext(message)
-            return message
+            tmsg = message
         if self._output_charset:
             return tmsg.encode(self._output_charset)
         return tmsg.encode(locale.getpreferredencoding())
@@ -424,16 +429,16 @@ class GNUTranslations(NullTranslations):
     def lngettext(self, msgid1, msgid2, n):
         try:
             tmsg = self._catalog[(msgid1, self.plural(n))]
-            if self._output_charset:
-                return tmsg.encode(self._output_charset)
-            return tmsg.encode(locale.getpreferredencoding())
         except KeyError:
             if self._fallback:
                 return self._fallback.lngettext(msgid1, msgid2, n)
             if n == 1:
-                return msgid1
+                tmsg = msgid1
             else:
-                return msgid2
+                tmsg = msgid2
+        if self._output_charset:
+            return tmsg.encode(self._output_charset)
+        return tmsg.encode(locale.getpreferredencoding())
 
     def gettext(self, message):
         missing = object()
@@ -573,11 +578,11 @@ def dgettext(domain, message):
     return t.gettext(message)
 
 def ldgettext(domain, message):
+    codeset = _localecodesets.get(domain)
     try:
-        t = translation(domain, _localedirs.get(domain, None),
-                        codeset=_localecodesets.get(domain))
+        t = translation(domain, _localedirs.get(domain, None), codeset=codeset)
     except OSError:
-        return message
+        return message.encode(codeset or locale.getpreferredencoding())
     return t.lgettext(message)
 
 def dngettext(domain, msgid1, msgid2, n):
@@ -592,14 +597,15 @@ def dngettext(domain, msgid1, msgid2, n):
     return t.ngettext(msgid1, msgid2, n)
 
 def ldngettext(domain, msgid1, msgid2, n):
+    codeset = _localecodesets.get(domain)
     try:
-        t = translation(domain, _localedirs.get(domain, None),
-                        codeset=_localecodesets.get(domain))
+        t = translation(domain, _localedirs.get(domain, None), codeset=codeset)
     except OSError:
         if n == 1:
-            return msgid1
+            tmsg = msgid1
         else:
-            return msgid2
+            tmsg = msgid2
+        return tmsg.encode(codeset or locale.getpreferredencoding())
     return t.lngettext(msgid1, msgid2, n)
 
 def gettext(message):
index 0c36664..d7d7e1e 100644 (file)
@@ -27,9 +27,9 @@ IDLEfork ChangeLog
 
        * INSTALLATION, setup.py: INSTALLATION: Remove the coexist.patch
        instructions
-       
+
        **************** setup.py:
-       
+
        Remove the idles script, add some words on IDLE Fork to the
        long_description, and clean up some line spacing.
 
@@ -42,30 +42,30 @@ IDLEfork ChangeLog
        * PyShell.py, idle, idles: Implement idle command interface as
        suggested by GvR [idle-dev] 16 July **************** PyShell: Added
        functionality:
-       
+
        usage: idle.py [-c command] [-d] [-i] [-r script] [-s] [-t title]
        [arg] ...
-       
+
        idle file(s)    (without options) edit the file(s)
-       
+
        -c cmd     run the command in a shell -d         enable the
        debugger -i         open an interactive shell -i file(s) open a
        shell and also an editor window for each file -r script  run a file
        as a script in a shell -s         run $IDLESTARTUP or
        $PYTHONSTARTUP before anything else -t title   set title of shell
        window
-       
+
        Remaining arguments are applied to the command (-c) or script (-r).
-       
+
        ****************** idles: Removed the idles script, not needed
-       
+
        ****************** idle:  Removed the IdleConf references, not
        required anymore
 
 2001-07-16 17:08  kbk
 
        * INSTALLATION, coexist.patch: Added installation instructions.
-       
+
        Added a patch which modifies idlefork so that it can co-exist with
        "official" IDLE in the site-packages directory. This patch is not
        necessary if only idlefork IDLE is installed. See INSTALLATION for
@@ -74,7 +74,7 @@ IDLEfork ChangeLog
 2001-07-16 15:50  kbk
 
        * idles: Add a script "idles" which opens a Python Shell window.
-       
+
        The default behaviour of idlefork idle is to open an editor window
        instead of a shell. Complex expressions may be run in a fresh
        environment by selecting "run".  There are times, however, when a
@@ -90,7 +90,7 @@ IDLEfork ChangeLog
 
        * PyShell.py, setup.py: Add a script "idles" which opens a Python
        Shell window.
-       
+
        The default behaviour of idlefork idle is to open an editor window
        instead of a shell. Complex expressions may be run in a fresh
        environment by selecting "run".  There are times, however, when a
@@ -110,13 +110,13 @@ IDLEfork ChangeLog
 
        * setup.py: Installing Idle to site-packages via Distutils does not
        copy the Idle help.txt file.
-       
+
        Ref SF Python Patch 422471
 
 2001-07-14 15:26  kbk
 
        * keydefs.py: py-cvs-2001_07_13 (Rev 1.3) merge
-       
+
        "Make copy, cut and paste events case insensitive.  Reported by
        Patrick K. O'Brien on idle-dev. (Should other bindings follow
        suit?)" --GvR
@@ -124,7 +124,7 @@ IDLEfork ChangeLog
 2001-07-14 15:21  kbk
 
        * idle.py: py-cvs-2001_07_13 (Rev 1.4) merge
-       
+
        "Move the action of loading the configuration to the IdleConf
        module rather than the idle.py script.  This has advantages and
        disadvantages; the biggest advantage being that we can more easily
@@ -133,21 +133,21 @@ IDLEfork ChangeLog
 2001-07-14 15:18  kbk
 
        * extend.txt: py-cvs-2001_07_13 (Rev 1.4) merge
-       
+
        "Quick update to the extension mechanism (extend.py is gone, long
        live config.txt)" --GvR
 
 2001-07-14 15:15  kbk
 
        * StackViewer.py: py-cvs-2001_07_13 (Rev 1.16) merge
-       
+
        "Refactored, with some future plans in mind. This now uses the new
        gotofileline() method defined in FileList.py"  --GvR
 
 2001-07-14 15:10  kbk
 
        * PyShell.py: py-cvs-2001_07_13 (Rev 1.34) merge
-       
+
        "Amazing.  A very subtle change in policy in descr-branch actually
        found a bug here.  Here's the deal: Class PyShell derives from
        class OutputWindow.  Method PyShell.close() wants to invoke its
@@ -166,19 +166,19 @@ IDLEfork ChangeLog
 2001-07-14 14:59  kbk
 
        * PyParse.py: py-cvs-2001_07_13 (Rel 1.9) merge
-       
+
        "Taught IDLE's autoident parser that "yield" is a keyword that
        begins a stmt.  Along w/ the preceding change to keyword.py, making
        all this work w/ a future-stmt just looks harder and harder."
        --tim_one
-       
+
        (From Rel 1.8: "Hack to make this still work with Python 1.5.2.
        ;-( " --fdrake)
 
 2001-07-14 14:51  kbk
 
        * IdleConf.py: py-cvs-2001_07_13 (Rel 1.7) merge
-       
+
        "Move the action of loading the configuration to the IdleConf
        module rather than the idle.py script.  This has advantages and
        disadvantages; the biggest advantage being that we can more easily
@@ -187,11 +187,11 @@ IDLEfork ChangeLog
 2001-07-14 14:45  kbk
 
        * FileList.py: py-cvs-2000_07_13 (Rev 1.9) merge
-       
+
        "Delete goodname() method, which is unused. Add gotofileline(), a
        convenience method which I intend to use in a variant. Rename
        test() to _test()."  --GvR
-       
+
        This was an interesting merge. The join completely missed removing
        goodname(), which was adjacent, but outside of, a small conflict.
        I only caught it by comparing the 1.1.3.2/1.1.3.3 diff.  CVS ain't
@@ -245,13 +245,13 @@ IDLEfork ChangeLog
 2001-07-14 10:13  kbk
 
        * PyShell.py: cvs-py-rel2_1 (Rev 1.29 - 1.33) merge
-       
+
        Merged the following py-cvs revs without conflict: 1.29 Reduce
        copyright text output at startup 1.30 Delay setting sys.args until
        Tkinter is fully initialized 1.31 Whitespace normalization 1.32
        Turn syntax warning into error when interactive 1.33 Fix warning
        initialization bug
-       
+
        Note that module is extensively modified wrt py-cvs
 
 2001-07-14 06:33  kbk
@@ -317,14 +317,14 @@ IDLEfork ChangeLog
 2001-07-13 13:35  kbk
 
        * EditorWindow.py: py-cvs-rel2_1 (Rev 1.33 - 1.37) merge
-       
+
        VP IDLE version depended on VP's ExecBinding.py and spawn.py to get
        the path to the Windows Doc directory (relative to python.exe).
        Removed this conflicting code in favor of py-cvs updates which on
        Windows use a hard coded path relative to the location of this
        module. py-cvs updates include support for webbrowser.py.  Module
        still has BrowserControl.py for 1.5.2 support.
-       
+
        At this point, the differences wrt py-cvs relate to menu
        functionality.
 
@@ -1194,7 +1194,7 @@ Wed Mar 10 05:18:02 1999  Guido van Rossum  <guido@cnri.reston.va.us>
 ======================================================================
        Python release 1.5.2b2, IDLE version 0.3
 ======================================================================
-       
+
 Wed Feb 17 22:47:41 1999  Guido van Rossum  <guido@cnri.reston.va.us>
 
        * NEWS.txt: News in 0.3.
@@ -1330,7 +1330,7 @@ Sat Jan  9 22:01:33 1999  Guido van Rossum  <guido@cnri.reston.va.us>
 ======================================================================
        Python release 1.5.2b1, IDLE version 0.2
 ======================================================================
-       
+
 Fri Jan  8 17:26:02 1999  Guido van Rossum  <guido@cnri.reston.va.us>
 
        * README.txt, NEWS.txt: What's new in this release.
index f12c185..f03e9cd 100644 (file)
@@ -1,5 +1,212 @@
+What's New in IDLE 3.6.3
+Released on 2017-09-25?
+========================
+
+
+bpo-31493: Fix code context update and font update timers.
+Canceling timers prevents a warning message when test_idle completes.
+
+bpo-31488: Update non-key options in former extension classes.
+When applying configdialog changes, call .reload for each feature class.
+Change ParenMatch so updated options affect existing instances attached
+to existing editor windows.
+
+bpo-31477: Improve rstrip entry in IDLE doc.
+Strip Trailing Whitespace strips more than blank spaces.
+Multiline string literals are not skipped.
+
+bpo-31480: fix tests to pass with zzdummy extension disabled. (#3590)
+To see the example in action, enable it on options extensions tab.
+
+bpo-31421: Document how IDLE runs tkinter programs.
+IDLE calls tcl/tk update in the background in order to make live
+interaction and experimentatin with tkinter applications much easier.
+
+bpo-bpo-31414: Fix tk entry box tests by deleting first.
+Adding to an int entry is not the same as deleting and inserting
+because int('') will fail.  Patch by Terry Jan Reedy.
+
+bpo-27099: Convert IDLE's built-in 'extensions' to regular features.
+  About 10 IDLE features were implemented as supposedly optional
+extensions.  Their different behavior could be confusing or worse for
+users and not good for maintenance.  Hence the conversion.
+  The main difference for users is that user configurable key bindings
+for builtin features are now handled uniformly.  Now, editing a binding
+in a keyset only affects its value in the keyset.  All bindings are
+defined together in the system-specific default keysets in config-
+extensions.def.  All custom keysets are saved as a whole in config-
+extension.cfg.  All take effect as soon as one clicks Apply or Ok.
+   The affected events are '<<force-open-completions>>',
+'<<expand-word>>', '<<force-open-calltip>>', '<<flash-paren>>',
+'<<format-paragraph>>', '<<run-module>>', '<<check-module>>', and
+'<<zoom-height>>'.  Any (global) customizations made before 3.6.3 will
+not affect their keyset-specific customization after 3.6.3. and vice
+versa.
+  Inital patch by Charles Wohlganger, revised by Terry Jan Reedy.
+
+bpo-31051:  Rearrange condigdialog General tab.
+Sort non-Help options into Window (Shell+Editor) and Editor (only).
+Leave room for the addition of new options.
+Patch by Terry Jan Reedy.
+
+bpo-30617: Add docstrings and tests for outwin subclass of editor.
+Move some data and functions from the class to module level.
+Patch by Cheryl Sabella.
+
+bpo-31287: Do not modify tkinter.messagebox in test_configdialog.
+Instead, mask it with an instance mock that can be deleted.
+Patch by Terry Jan Reedy.
+
+bpo-30781: Use ttk widgets in ConfigDialog pages.
+These should especially look better on MacOSX.
+Patches by Terry Jan Reedy and Cheryl Sabella.
+
+bpo-31206: Factor HighPage(Frame) class from ConfigDialog.
+Patch by Cheryl Sabella.
+
+bp0-31001: Add tests for configdialog highlight tab.
+Patch by Cheryl Sabella.
+
+bpo-31205: Factor KeysPage(Frame) class from ConfigDialog.
+The slightly modified tests continue to pass.
+Patch by Cheryl Sabella.
+
+bpo-31002: Add tests for configdialog keys tab.
+Patch by Cheryl Sabella.
+
+bpo-19903: Change calltipes to use inspect.signature.
+Idlelib.calltips.get_argspec now uses inspect.signature instead of
+inspect.getfullargspec, like help() does.  This improves the signature
+in the call tip in a few different cases, including builtins converted
+to provide a signature.  A message is added if the object is not
+callable, has an invalid signature, or if it has positional-only
+parameters.  Patch by Louie Lu.
+
+bop-31083: Add an outline of a TabPage class in configdialog.
+Add template as comment. Update existing classes to match outline.
+Initial patch by Cheryl Sabella.
+
+bpo-31050: Factor GenPage(Frame) class from ConfigDialog.
+The slightly modified tests for the General tab continue to pass.
+Patch by Cheryl Sabella.
+
+bpo-31004: Factor FontPage(Frame) class from ConfigDialog.
+The slightly modified tests continue to pass. The General test
+broken by the switch to ttk.Notebook is fixed.
+Patch mostly by Cheryl Sabella.
+
+bpo-30781: IDLE - Use ttk Notebook in ConfigDialog.
+This improves navigation by tabbing.
+Patch by Terry Jan Reedy.
+
+bpo-31060: IDLE - Finish rearranging methods of ConfigDialog.
+Grouping methods pertaining to each tab and the buttons will aid
+writing tests and improving the tabs and will enable splitting the
+groups into classes.
+Patch by Terry Jan Reedy.
+
+bpo-30853: IDLE -- Factor a VarTrace class out of ConfigDialog.
+Instance tracers manages pairs consisting of a tk variable and a
+callback function.  When tracing is turned on, setting the variable
+calls the function.  Test coverage for the new class is 100%.
+Patch by Terry Jan Reedy.
+
+bpo-31003: IDLE: Add more tests for General tab.
+Patch by Terry Jan Reedy.
+
+bpo-30993: IDLE - Improve configdialog font page and tests.
+*In configdialog: Document causal pathways in create_font_tab
+docstring.  Simplify some attribute names. Move set_samples calls to
+var_changed_font (idea from Cheryl Sabella).  Move related functions to
+positions after the create widgets function.
+* In test_configdialog: Fix test_font_set so not order dependent.  Fix
+renamed test_indent_scale so it tests the widget.  Adjust tests for
+movement of set_samples call.  Add tests for load functions.  Put all
+font tests in one class and tab indent tests in another.  Except for
+two lines, these tests completely cover the related functions.
+Patch by Terry Jan Reedy.
+
+bpo-30981: IDLE -- Add more configdialog font page tests.
+
+bpo-28523: IDLE: replace 'colour' with 'color' in configdialog.
+
+bpo-30917: Add tests for idlelib.config.IdleConf.
+Increase coverage from 46% to 96%.
+Patch by Louie Lu.
+
+bpo-30913: Document ConfigDialog tk Vars, methods, and widgets in docstrings
+This will facilitate improving the dialog and splitting up the class.
+Original patch by Cheryl Sabella.
+
+bpo-30899: Add tests for ConfigParser subclasses in config.
+Coverage is 100% for those classes and ConfigChanges.
+Patch by Louie Lu.
+
+bpo-30881: Add docstrings to browser.py.
+Patch by Cheryl Sabella.
+
+bpo-30851: Remove unused tk variables in configdialog.
+One is a duplicate, one is set but cannot be altered by users.
+Patch by Cheryl Sabella.
+
+bpo-30870: Select font option with Up and Down keys, as well as with mouse.
+Added test increases configdialog coverage to 60%
+Patches mostly by Louie Lu.
+
+bpo-8231: Call config.IdleConf.GetUserCfgDir only once per process.
+
+bpo-30779: Factor ConfigChanges class from configdialog, put in config; test.
+* In config, put dump test code in a function; run it and unittest in
+  'if __name__ == '__main__'.
+* Add class config.ConfigChanges based on changes_class_v4.py on bpo issue.
+* Add class test_config.ChangesTest, partly using configdialog_tests_v1.py.
+* Revise configdialog to use ConfigChanges; see tracker msg297804.
+* Revise test_configdialog to match configdialog changes.
+* Remove configdialog functions unused or moved to ConfigChanges.
+Cheryl Sabella contributed parts of the patch.
+
+bpo-30777: Configdialog - add docstrings and improve comments.
+Patch by Cheryl Sabella.
+
+bpo-30495: Improve textview with docstrings, PEP8 names, and more tests.
+Split TextViewer class into ViewWindow, ViewFrame, and TextFrame classes
+so that instances of the latter two can be placed with other widgets
+within a multiframe window.
+Patches by Cheryl Sabella and Terry Jan Reedy.
+
+bpo-30723: Make several improvements to parenmatch.
+* Add 'parens' style to highlight both opener and closer.
+* Make 'default' style, which is not default, a synonym for 'opener'.
+* Make time-delay work the same with all styles.
+* Add help for config dialog extensions tab, including parenmatch.
+* Add new tests.
+Original patch by Charles Wohlganger.  Revisions by Terry Jan Reedy
+
+bpo-30674: Grep -- Add docstrings.  Patch by Cheryl Sabella.
+
+bpo-21519: IDLE's basic custom key entry dialog now detects
+duplicates properly. Original patch by Saimadhav Heblikar.
+
+bpo-29910: IDLE no longer deletes a character after commenting out a
+region by a key shortcut.  Add "return 'break'" for this and other
+potential conflicts between IDLE and default key bindings.
+Patch by Serhiy Storchaka.
+
+bpo-30728: Modernize idlelib.configdialog:
+* replace import * with specific imports;
+* lowercase method and attribute lines.
+Patch by Cheryl Sabella.
+
+bpo-6739: Verify user-entered key sequences by trying to bind them
+with to a tk widget.  Add tests for all 3 validation functions.
+Original patch by G Polo.  Tests added by Cheryl Sabella.
+Code revised and more tests added by Terry Jan Reedy
+
+bpo-24813: Add icon to help_about and make other changes.
+
+
 What's New in IDLE 3.6.2
-Released on 2017-06-30?
+Released on 2017-07-11
 ========================
 
 bpo-15786: Fix several problems with IDLE's autocompletion box.
@@ -29,7 +236,7 @@ What's New in IDLE 3.6.1
 Released on 2017-03-21
 ========================
 
-Issue #29071: IDLE colors f-string prefixes (but not invalid ur prefixes).
+Issue #29071: IDLE colors f-string prefixes but not invalid ur prefixes.
 
 Issue #28572: Add 10% to coverage of IDLE's test_configdialog.
 Update and augment description of the configuration system.
index 51e8ef5..c784a1a 100644 (file)
@@ -206,7 +206,7 @@ Window
   <open windows>   # windows
 
 Help
-  About IDLE       # eEW.about_dialog, help_about.AboutDialog 
+  About IDLE       # eEW.about_dialog, help_about.AboutDialog
   ---
   IDLE Help        # eEW.help_dialog, helpshow_idlehelp
   Python Doc       # eEW.python_docs
@@ -230,7 +230,7 @@ Help
 <No menu>
 Center Insert      # eEW.center_insert_event
 
-  
+
 CODE STYLE -- Generally PEP 8.
 
 import
index 1e44fa5..edf445f 100644 (file)
@@ -1,7 +1,7 @@
-"""autocomplete.py - An IDLE extension for automatically completing names.
+"""Complete either attribute names or file names.
 
-This extension can complete either attribute names or file names. It can pop
-a window with all available names, for the user to select from.
+Either on demand or after a user-selected delay after a key character,
+pop up a list of candidates.
 """
 import os
 import string
@@ -27,18 +27,9 @@ if os.altsep:  # e.g. '/' on Windows...
 
 class AutoComplete:
 
-    menudefs = [
-        ('edit', [
-            ("Show Completions", "<<force-open-completions>>"),
-        ])
-    ]
-
-    popupwait = idleConf.GetOption("extensions", "AutoComplete",
-                                   "popupwait", type="int", default=0)
-
     def __init__(self, editwin=None):
         self.editwin = editwin
-        if editwin is not None:  # not in subprocess or test
+        if editwin is not None:   # not in subprocess or test
             self.text = editwin.text
             self.autocompletewindow = None
             # id of delayed call, and the index of the text insert when
@@ -47,6 +38,11 @@ class AutoComplete:
             self._delayed_completion_id = None
             self._delayed_completion_index = None
 
+    @classmethod
+    def reload(cls):
+        cls.popupwait = idleConf.GetOption(
+            "extensions", "AutoComplete", "popupwait", type="int", default=0)
+
     def _make_autocomplete_window(self):
         return autocomplete_w.AutoCompleteWindow(self.text)
 
@@ -60,6 +56,7 @@ class AutoComplete:
         if a function call is needed.
         """
         self.open_completions(True, False, True)
+        return "break"
 
     def try_open_completions_event(self, event):
         """Happens when it would be nice to open a completion list, but not
@@ -227,6 +224,9 @@ class AutoComplete:
         return eval(name, namespace)
 
 
+AutoComplete.reload()
+
+
 if __name__ == '__main__':
     from unittest import main
     main('idlelib.idle_test.test_autocomplete', verbosity=2)
index 6b46bee..42e733a 100644 (file)
@@ -10,23 +10,13 @@ Changing the current text line or leaving the cursor in a different
 place before requesting the next selection causes AutoExpand to reset
 its state.
 
-This is an extension file and there is only one instance of AutoExpand.
+There is only one instance of Autoexpand.
 '''
 import re
 import string
 
-###$ event <<expand-word>>
-###$ win <Alt-slash>
-###$ unix <Alt-slash>
 
 class AutoExpand:
-
-    menudefs = [
-        ('edit', [
-            ('E_xpand Word', '<<expand-word>>'),
-         ]),
-    ]
-
     wordchars = string.ascii_letters + string.digits + "_"
 
     def __init__(self, editwin):
@@ -100,6 +90,7 @@ class AutoExpand:
             i = i-1
         return line[i:]
 
+
 if __name__ == '__main__':
     import unittest
     unittest.main('idlelib.idle_test.test_autoexpand', verbosity=2)
index ea05638..4cf4744 100644 (file)
@@ -23,12 +23,28 @@ file_open = None  # Method...Item and Class...Item use this.
 # Normally pyshell.flist.open, but there is no pyshell.flist for htest.
 
 class ClassBrowser:
+    """Browse module classes and functions in IDLE.
+    """
 
     def __init__(self, flist, name, path, _htest=False):
         # XXX This API should change, if the file doesn't end in ".py"
         # XXX the code here is bogus!
-        """
-        _htest - bool, change box when location running htest.
+        """Create a window for browsing a module's structure.
+
+        Args:
+            flist: filelist.FileList instance used as the root for the window.
+            name: Python module to parse.
+            path: Module search path.
+            _htest - bool, change box when location running htest.
+
+        Global variables:
+            file_open: Function used for opening a file.
+
+        Instance variables:
+            name: Module name.
+            file: Full path and module with .py extension.  Used in
+                creating ModuleBrowserTreeItem as the rootnode for
+                the tree and subsequently in the children.
         """
         global file_open
         if not _htest:
@@ -39,10 +55,12 @@ class ClassBrowser:
         self.init(flist)
 
     def close(self, event=None):
+        "Dismiss the window and the tree nodes."
         self.top.destroy()
         self.node.destroy()
 
     def init(self, flist):
+        "Create browser tkinter widgets, including the tree."
         self.flist = flist
         # reset pyclbr
         pyclbr._modules.clear()
@@ -66,24 +84,42 @@ class ClassBrowser:
         node.expand()
 
     def settitle(self):
+        "Set the window title."
         self.top.wm_title("Class Browser - " + self.name)
         self.top.wm_iconname("Class Browser")
 
     def rootnode(self):
+        "Return a ModuleBrowserTreeItem as the root of the tree."
         return ModuleBrowserTreeItem(self.file)
 
 class ModuleBrowserTreeItem(TreeItem):
+    """Browser tree for Python module.
+
+    Uses TreeItem as the basis for the structure of the tree.
+    """
 
     def __init__(self, file):
+        """Create a TreeItem for the file.
+
+        Args:
+            file: Full path and module name.
+        """
         self.file = file
 
     def GetText(self):
+        "Return the module name as the text string to display."
         return os.path.basename(self.file)
 
     def GetIconName(self):
+        "Return the name of the icon to display."
         return "python"
 
     def GetSubList(self):
+        """Return the list of ClassBrowserTreeItem items.
+
+        Each item returned from listclasses is the first level of
+        classes/functions within the module.
+        """
         sublist = []
         for name in self.listclasses():
             item = ClassBrowserTreeItem(name, self.classes, self.file)
@@ -91,6 +127,7 @@ class ModuleBrowserTreeItem(TreeItem):
         return sublist
 
     def OnDoubleClick(self):
+        "Open a module in an editor window when double clicked."
         if os.path.normcase(self.file[-3:]) != ".py":
             return
         if not os.path.exists(self.file):
@@ -98,9 +135,20 @@ class ModuleBrowserTreeItem(TreeItem):
         pyshell.flist.open(self.file)
 
     def IsExpandable(self):
+        "Return True if Python (.py) file."
         return os.path.normcase(self.file[-3:]) == ".py"
 
     def listclasses(self):
+        """Return list of classes and functions in the module.
+
+        The dictionary output from pyclbr is re-written as a
+        list of tuples in the form (lineno, name) and
+        then sorted so that the classes and functions are
+        processed in line number order.  The returned list only
+        contains the name and not the line number.  An instance
+        variable self.classes contains the pyclbr dictionary values,
+        which are instances of Class and Function.
+        """
         dir, file = os.path.split(self.file)
         name, ext = os.path.splitext(file)
         if os.path.normcase(ext) != ".py":
@@ -134,9 +182,25 @@ class ModuleBrowserTreeItem(TreeItem):
         return list
 
 class ClassBrowserTreeItem(TreeItem):
+    """Browser tree for classes within a module.
+
+    Uses TreeItem as the basis for the structure of the tree.
+    """
 
     def __init__(self, name, classes, file):
+        """Create a TreeItem for the class/function.
+
+        Args:
+            name: Name of the class/function.
+            classes: Dictonary of Class/Function instances from pyclbr.
+            file: Full path and module name.
+
+        Instance variables:
+            self.cl: Class/Function instance for the class/function name.
+            self.isfunction: True if self.cl is a Function.
+        """
         self.name = name
+        # XXX - Does classes need to be an instance variable?
         self.classes = classes
         self.file = file
         try:
@@ -146,25 +210,33 @@ class ClassBrowserTreeItem(TreeItem):
         self.isfunction = isinstance(self.cl, pyclbr.Function)
 
     def GetText(self):
+        "Return the name of the function/class to display."
         if self.isfunction:
             return "def " + self.name + "(...)"
         else:
             return "class " + self.name
 
     def GetIconName(self):
+        "Return the name of the icon to display."
         if self.isfunction:
             return "python"
         else:
             return "folder"
 
     def IsExpandable(self):
+        "Return True if this class has methods."
         if self.cl:
             try:
                 return not not self.cl.methods
             except AttributeError:
                 return False
+        return None
 
     def GetSubList(self):
+        """Return Class methods as a list of MethodBrowserTreeItem items.
+
+        Each item is a method within the class.
+        """
         if not self.cl:
             return []
         sublist = []
@@ -174,6 +246,7 @@ class ClassBrowserTreeItem(TreeItem):
         return sublist
 
     def OnDoubleClick(self):
+        "Open module with file_open and position to lineno, if it exists."
         if not os.path.exists(self.file):
             return
         edit = file_open(self.file)
@@ -182,6 +255,7 @@ class ClassBrowserTreeItem(TreeItem):
             edit.gotoline(lineno)
 
     def listmethods(self):
+        "Return list of methods within a class sorted by lineno."
         if not self.cl:
             return []
         items = []
@@ -194,28 +268,43 @@ class ClassBrowserTreeItem(TreeItem):
         return list
 
 class MethodBrowserTreeItem(TreeItem):
+    """Browser tree for methods within a class.
+
+    Uses TreeItem as the basis for the structure of the tree.
+    """
 
     def __init__(self, name, cl, file):
+        """Create a TreeItem for the methods.
+
+        Args:
+            name: Name of the class/function.
+            cl: pyclbr.Class instance for name.
+            file: Full path and module name.
+        """
         self.name = name
         self.cl = cl
         self.file = file
 
     def GetText(self):
+        "Return the method name to display."
         return "def " + self.name + "(...)"
 
     def GetIconName(self):
-        return "python" # XXX
+        "Return the name of the icon to display."
+        return "python"
 
     def IsExpandable(self):
-        return 0
+        "Return False as there are no tree items after methods."
+        return False
 
     def OnDoubleClick(self):
+        "Open module with file_open and position at the method start."
         if not os.path.exists(self.file):
             return
         edit = file_open(self.file)
         edit.gotoline(self.cl.methods[self.name])
 
-def _class_browser(parent): #Wrapper for htest
+def _class_browser(parent): # htest #
     try:
         file = __file__
     except NameError:
index c7361d1..bf967f4 100644 (file)
@@ -89,24 +89,27 @@ class CallTip:
             # If the event was triggered by the same event that unbinded
             # this function, the function will be called nevertheless,
             # so do nothing in this case.
-            return
+            return None
         curline, curcol = map(int, self.widget.index("insert").split('.'))
         if curline < self.parenline or \
            (curline == self.parenline and curcol <= self.parencol) or \
            self.widget.compare("insert", ">", MARK_RIGHT):
             self.hidetip()
+            return "break"
         else:
             self.position_window()
             if self.checkhide_after_id is not None:
                 self.widget.after_cancel(self.checkhide_after_id)
             self.checkhide_after_id = \
                 self.widget.after(CHECKHIDE_TIME, self.checkhide_event)
+            return None
 
     def hide_event(self, event):
         if not self.tipwindow:
             # See the explanation in checkhide_event.
-            return
+            return None
         self.hidetip()
+        return "break"
 
     def hidetip(self):
         if not self.tipwindow:
index 4c5aea2..ec8f616 100644 (file)
@@ -1,9 +1,8 @@
-"""calltips.py - An IDLE Extension to Jog Your Memory
+"""Pop up a reminder of how to call a function.
 
 Call Tips are floating windows which display function, class, and method
 parameter and docstring information when you type an opening parenthesis, and
 which disappear when you type a closing parenthesis.
-
 """
 import inspect
 import re
@@ -15,13 +14,8 @@ from idlelib import calltip_w
 from idlelib.hyperparser import HyperParser
 import __main__
 
-class CallTips:
 
-    menudefs = [
-        ('edit', [
-            ("Show call tip", "<<force-open-calltip>>"),
-        ])
-    ]
+class CallTips:
 
     def __init__(self, editwin=None):
         if editwin is None:  # subprocess and test
@@ -47,6 +41,7 @@ class CallTips:
     def force_open_calltip_event(self, event):
         "The user selected the menu entry or hotkey, open the tip."
         self.open_calltip(True)
+        return "break"
 
     def try_open_calltip_event(self, event):
         """Happens when it would be nice to open a CallTip, but not really
@@ -102,6 +97,7 @@ class CallTips:
         else:
             return get_argspec(get_entity(expression))
 
+
 def get_entity(expression):
     """Return the object corresponding to expression evaluated
     in a namespace spanning sys.modules and __main.dict__.
@@ -122,7 +118,8 @@ _MAX_LINES = 5  # enough for bytes
 _INDENT = ' '*4  # for wrapped signatures
 _first_param = re.compile(r'(?<=\()\w*\,?\s*')
 _default_callable_argspec = "See source or doc"
-
+_invalid_method = "invalid method signature"
+_argument_positional = "\n['/' marks preceding arguments as positional-only]\n"
 
 def get_argspec(ob):
     '''Return a string describing the signature of a callable object, or ''.
@@ -133,25 +130,30 @@ def get_argspec(ob):
     empty line or _MAX_LINES.    For builtins, this typically includes
     the arguments in addition to the return value.
     '''
-    argspec = ""
+    argspec = default = ""
     try:
         ob_call = ob.__call__
     except BaseException:
-        return argspec
-    if isinstance(ob, type):
-        fob = ob.__init__
-    elif isinstance(ob_call, types.MethodType):
-        fob = ob_call
-    else:
-        fob = ob
-    if isinstance(fob, (types.FunctionType, types.MethodType)):
-        argspec = inspect.formatargspec(*inspect.getfullargspec(fob))
-        if (isinstance(ob, (type, types.MethodType)) or
-                isinstance(ob_call, types.MethodType)):
-            argspec = _first_param.sub("", argspec)
+        return default
+
+    fob = ob_call if isinstance(ob_call, types.MethodType) else ob
+
+    try:
+        argspec = str(inspect.signature(fob))
+    except ValueError as err:
+        msg = str(err)
+        if msg.startswith(_invalid_method):
+            return _invalid_method
+
+    if '/' in argspec:
+        """Using AC's positional argument should add the explain"""
+        argspec += _argument_positional
+    if isinstance(fob, type) and argspec == '()':
+        """fob with no argument, use default callable argspec"""
+        argspec = _default_callable_argspec
 
     lines = (textwrap.wrap(argspec, _MAX_COLS, subsequent_indent=_INDENT)
-            if len(argspec) > _MAX_COLS else [argspec] if argspec else [])
+             if len(argspec) > _MAX_COLS else [argspec] if argspec else [])
 
     if isinstance(ob_call, types.MethodType):
         doc = ob_call.__doc__
@@ -170,6 +172,7 @@ def get_argspec(ob):
         argspec = _default_callable_argspec
     return argspec
 
+
 if __name__ == '__main__':
     from unittest import main
     main('idlelib.idle_test.test_calltips', verbosity=2)
index f25e1b3..316e510 100644 (file)
@@ -1,4 +1,4 @@
-"""codecontext - Extension to display the block context above the edit window
+"""codecontext - display the block context above the edit window
 
 Once code has scrolled off the top of a window, it can be difficult to
 determine which block you are in.  This extension implements a pane at the top
@@ -22,17 +22,14 @@ BLOCKOPENERS = {"class", "def", "elif", "else", "except", "finally", "for",
 UPDATEINTERVAL = 100 # millisec
 FONTUPDATEINTERVAL = 1000 # millisec
 
-getspacesfirstword =\
-                   lambda s, c=re.compile(r"^(\s*)(\w*)"): c.match(s).groups()
+def getspacesfirstword(s, c=re.compile(r"^(\s*)(\w*)")):
+    return c.match(s).groups()
+
 
 class CodeContext:
-    menudefs = [('options', [('!Code Conte_xt', '<<toggle-code-context>>')])]
-    context_depth = idleConf.GetOption("extensions", "CodeContext",
-                                       "numlines", type="int", default=3)
-    bgcolor = idleConf.GetOption("extensions", "CodeContext",
-                                 "bgcolor", type="str", default="LightGray")
-    fgcolor = idleConf.GetOption("extensions", "CodeContext",
-                                 "fgcolor", type="str", default="Black")
+    bgcolor = "LightGray"
+    fgcolor = "Black"
+
     def __init__(self, editwin):
         self.editwin = editwin
         self.text = editwin.text
@@ -45,14 +42,25 @@ class CodeContext:
         # starts the toplevel 'block' of the module.
         self.info = [(0, -1, "", False)]
         self.topvisible = 1
-        visible = idleConf.GetOption("extensions", "CodeContext",
-                                     "visible", type="bool", default=False)
-        if visible:
-            self.toggle_code_context_event()
-            self.editwin.setvar('<<toggle-code-context>>', True)
         # Start two update cycles, one for context lines, one for font changes.
-        self.text.after(UPDATEINTERVAL, self.timer_event)
-        self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
+        self.t1 = self.text.after(UPDATEINTERVAL, self.timer_event)
+        self.t2 = self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
+
+    @classmethod
+    def reload(cls):
+        cls.context_depth = idleConf.GetOption("extensions", "CodeContext",
+                                       "numlines", type="int", default=3)
+##        cls.bgcolor = idleConf.GetOption("extensions", "CodeContext",
+##                                     "bgcolor", type="str", default="LightGray")
+##        cls.fgcolor = idleConf.GetOption("extensions", "CodeContext",
+##                                     "fgcolor", type="str", default="Black")
+
+    def __del__(self):
+        try:
+            self.text.after_cancel(self.t1)
+            self.text.after_cancel(self.t2)
+        except:
+            pass
 
     def toggle_code_context_event(self, event=None):
         if not self.label:
@@ -71,14 +79,12 @@ class CodeContext:
             border = 0
             for widget in widgets:
                 border += widget.tk.getint(widget.cget('border'))
-            self.label = tkinter.Label(self.editwin.top,
-                                       text="\n" * (self.context_depth - 1),
-                                       anchor=W, justify=LEFT,
-                                       font=self.textfont,
-                                       bg=self.bgcolor, fg=self.fgcolor,
-                                       width=1, #don't request more than we get
-                                       padx=padx, border=border,
-                                       relief=SUNKEN)
+            self.label = tkinter.Label(
+                    self.editwin.top, text="\n" * (self.context_depth - 1),
+                    anchor=W, justify=LEFT, font=self.textfont,
+                    bg=self.bgcolor, fg=self.fgcolor,
+                    width=1, #don't request more than we get
+                    padx=padx, border=border, relief=SUNKEN)
             # Pack the label widget before and above the text_frame widget,
             # thus ensuring that it will appear directly above text_frame
             self.label.pack(side=TOP, fill=X, expand=False,
@@ -86,9 +92,7 @@ class CodeContext:
         else:
             self.label.destroy()
             self.label = None
-        idleConf.SetOption("extensions", "CodeContext", "visible",
-                           str(self.label is not None))
-        idleConf.SaveUserCfgFiles()
+        return "break"
 
     def get_line_info(self, linenum):
         """Get the line indent value, text, and any block start keyword
@@ -168,11 +172,14 @@ class CodeContext:
     def timer_event(self):
         if self.label:
             self.update_code_context()
-        self.text.after(UPDATEINTERVAL, self.timer_event)
+        self.t1 = self.text.after(UPDATEINTERVAL, self.timer_event)
 
     def font_timer_event(self):
         newtextfont = self.text["font"]
         if self.label and newtextfont != self.textfont:
             self.textfont = newtextfont
             self.label["font"] = self.textfont
-        self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
+        self.t2 = self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
+
+
+CodeContext.reload()
index a24b8c9..8689fd9 100644 (file)
@@ -1,5 +1,25 @@
 # config-extensions.def
 #
+# The following sections are for features that are no longer extensions.
+# Their options values are left here for back-compatibility.
+
+[AutoComplete]
+popupwait= 2000
+
+[CodeContext]
+numlines= 3
+visible= False
+bgcolor= LightGray
+fgcolor= Black
+
+[FormatParagraph]
+max-width= 72
+
+[ParenMatch]
+style= expression
+flash-delay= 500
+bell= True
+
 # IDLE reads several config files to determine user preferences.  This
 # file is the default configuration file for IDLE extensions settings.
 #
@@ -19,7 +39,7 @@
 # extension that may be sensibly re-configured.
 #
 # If there are no keybindings for a menus' virtual events, include lines
-# like <<toggle-code-context>>=  (See [CodeContext], below.)
+# like <<toggle-code-context>>=.
 #
 # Currently it is necessary to manually modify this file to change
 # extension key bindings and default values. To customize, create
 # See config-keys.def for notes on specifying keys and extend.txt for
 # information on creating IDLE extensions.
 
-[AutoComplete]
-enable=True
-popupwait=2000
-[AutoComplete_cfgBindings]
-force-open-completions=<Control-Key-space>
-[AutoComplete_bindings]
-autocomplete=<Key-Tab>
-try-open-completions=<KeyRelease-period> <KeyRelease-slash> <KeyRelease-backslash>
-
-[AutoExpand]
-enable=True
-[AutoExpand_cfgBindings]
-expand-word=<Alt-Key-slash>
-
-[CallTips]
-enable=True
-[CallTips_cfgBindings]
-force-open-calltip=<Control-Key-backslash>
-[CallTips_bindings]
-try-open-calltip=<KeyRelease-parenleft>
-refresh-calltip=<KeyRelease-parenright> <KeyRelease-0>
-
-[CodeContext]
-enable=True
-enable_shell=False
-numlines=3
-visible=False
-bgcolor=LightGray
-fgcolor=Black
-[CodeContext_bindings]
-toggle-code-context=
-
-[FormatParagraph]
-enable=True
-max-width=72
-[FormatParagraph_cfgBindings]
-format-paragraph=<Alt-Key-q>
-
-[ParenMatch]
-enable=True
-style= expression
-flash-delay= 500
-bell=True
-[ParenMatch_cfgBindings]
-flash-paren=<Control-Key-0>
-[ParenMatch_bindings]
-paren-closed=<KeyRelease-parenright> <KeyRelease-bracketright> <KeyRelease-braceright>
-
-[RstripExtension]
-enable=True
-enable_shell=False
-enable_editor=True
-
-[ScriptBinding]
-enable=True
-enable_shell=False
-enable_editor=True
-[ScriptBinding_cfgBindings]
-run-module=<Key-F5>
-check-module=<Alt-Key-x>
-
-[ZoomHeight]
-enable=True
-[ZoomHeight_cfgBindings]
-zoom-height=<Alt-Key-2>
+# A fake extension for testing and example purposes.  When enabled and
+# invoked, inserts or deletes z-text at beginning of every line.
+[ZzDummy]
+enable= False
+enable_shell = False
+enable_editor = True
+z-text= Z
+[ZzDummy_cfgBindings]
+z-in= <Control-Shift-KeyRelease-Insert>
+[ZzDummy_bindings]
+z-out= <Control-Shift-KeyRelease-Delete>
index 64788f9..fd23519 100644 (file)
@@ -57,6 +57,14 @@ toggle-tabs=<Alt-Key-t> <Meta-Key-t> <Alt-Key-T> <Meta-Key-T>
 change-indentwidth=<Alt-Key-u> <Meta-Key-u> <Alt-Key-U> <Meta-Key-U>
 del-word-left=<Control-Key-BackSpace>
 del-word-right=<Control-Key-Delete>
+force-open-completions= <Control-Key-space>
+expand-word= <Alt-Key-slash>
+force-open-calltip= <Control-Key-backslash>
+format-paragraph= <Alt-Key-q>
+flash-paren= <Control-Key-0>
+run-module= <Key-F5>
+check-module= <Alt-Key-x>
+zoom-height= <Alt-Key-2>
 
 [IDLE Classic Unix]
 copy=<Alt-Key-w> <Meta-Key-w>
@@ -108,6 +116,14 @@ toggle-tabs=<Alt-Key-t>
 change-indentwidth=<Alt-Key-u>
 del-word-left=<Alt-Key-BackSpace>
 del-word-right=<Alt-Key-d>
+force-open-completions= <Control-Key-space>
+expand-word= <Alt-Key-slash>
+force-open-calltip= <Control-Key-backslash>
+format-paragraph= <Alt-Key-q>
+flash-paren= <Control-Key-0>
+run-module= <Key-F5>
+check-module= <Alt-Key-x>
+zoom-height= <Alt-Key-2>
 
 [IDLE Modern Unix]
 copy = <Control-Shift-Key-C> <Control-Key-Insert>
@@ -159,6 +175,14 @@ toggle-tabs = <Control-Key-T>
 change-indentwidth = <Alt-Key-u>
 del-word-left = <Control-Key-BackSpace>
 del-word-right = <Control-Key-Delete>
+force-open-completions= <Control-Key-space>
+expand-word= <Alt-Key-slash>
+force-open-calltip= <Control-Key-backslash>
+format-paragraph= <Alt-Key-q>
+flash-paren= <Control-Key-0>
+run-module= <Key-F5>
+check-module= <Alt-Key-x>
+zoom-height= <Alt-Key-2>
 
 [IDLE Classic Mac]
 copy=<Command-Key-c>
@@ -210,6 +234,14 @@ toggle-tabs=<Control-Key-t>
 change-indentwidth=<Control-Key-u>
 del-word-left=<Control-Key-BackSpace>
 del-word-right=<Control-Key-Delete>
+force-open-completions= <Control-Key-space>
+expand-word= <Option-Key-slash>
+force-open-calltip= <Control-Key-backslash>
+format-paragraph= <Option-Key-q>
+flash-paren= <Control-Key-0>
+run-module= <Key-F5>
+check-module= <Option-Key-x>
+zoom-height= <Option-Key-0>
 
 [IDLE Classic OSX]
 toggle-tabs = <Control-Key-t>
@@ -262,4 +294,11 @@ python-context-help = <Shift-Key-F1>
 save-copy-of-window-as-file = <Option-Command-Key-s>
 open-window-from-file = <Command-Key-o>
 python-docs = <Key-F1>
-
+force-open-completions= <Control-Key-space>
+expand-word= <Option-Key-slash>
+force-open-calltip= <Control-Key-backslash>
+format-paragraph= <Option-Key-q>
+flash-paren= <Control-Key-0>
+run-module= <Key-F5>
+check-module= <Option-Key-x>
+zoom-height= <Option-Key-0>
index 6a9fc61..c3e57bc 100644 (file)
@@ -7,7 +7,7 @@ which duplicate the defaults will be removed from the user's
 configuration files, and if a user file becomes empty, it will be
 deleted.
 
-The configuration database maps options to values.  Comceptually, the
+The configuration database maps options to values.  Conceptually, the
 database keys are tuples (config-type, section, item).  As implemented,
 there are  separate dicts for default and user values.  Each has
 config-type keys 'main', 'extensions', 'highlight', and 'keys'.  The
@@ -30,6 +30,7 @@ import os
 import sys
 
 from tkinter.font import Font
+import idlelib
 
 class InvalidConfigType(Exception): pass
 class InvalidConfigSet(Exception): pass
@@ -44,7 +45,7 @@ class IdleConfParser(ConfigParser):
         """
         cfgFile - string, fully specified configuration file name
         """
-        self.file = cfgFile
+        self.file = cfgFile  # This is currently '' when testing.
         ConfigParser.__init__(self, defaults=cfgDefaults, strict=False)
 
     def Get(self, section, option, type=None, default=None, raw=False):
@@ -73,38 +74,14 @@ class IdleConfParser(ConfigParser):
 
     def Load(self):
         "Load the configuration file from disk."
-        self.read(self.file)
+        if self.file:
+            self.read(self.file)
 
 class IdleUserConfParser(IdleConfParser):
     """
     IdleConfigParser specialised for user configuration handling.
     """
 
-    def AddSection(self, section):
-        "If section doesn't exist, add it."
-        if not self.has_section(section):
-            self.add_section(section)
-
-    def RemoveEmptySections(self):
-        "Remove any sections that have no options."
-        for section in self.sections():
-            if not self.GetOptionList(section):
-                self.remove_section(section)
-
-    def IsEmpty(self):
-        "Return True if no sections after removing empty sections."
-        self.RemoveEmptySections()
-        return not self.sections()
-
-    def RemoveOption(self, section, option):
-        """Return True if option is removed from section, else False.
-
-        False if either section does not exist or did not have option.
-        """
-        if self.has_section(section):
-            return self.remove_option(section, option)
-        return False
-
     def SetOption(self, section, option, value):
         """Return True if option is added or changed to value, else False.
 
@@ -122,6 +99,31 @@ class IdleUserConfParser(IdleConfParser):
             self.set(section, option, value)
             return True
 
+    def RemoveOption(self, section, option):
+        """Return True if option is removed from section, else False.
+
+        False if either section does not exist or did not have option.
+        """
+        if self.has_section(section):
+            return self.remove_option(section, option)
+        return False
+
+    def AddSection(self, section):
+        "If section doesn't exist, add it."
+        if not self.has_section(section):
+            self.add_section(section)
+
+    def RemoveEmptySections(self):
+        "Remove any sections that have no options."
+        for section in self.sections():
+            if not self.GetOptionList(section):
+                self.remove_section(section)
+
+    def IsEmpty(self):
+        "Return True if no sections after removing empty sections."
+        self.RemoveEmptySections()
+        return not self.sections()
+
     def RemoveFile(self):
         "Remove user config file self.file from disk if it exists."
         if os.path.exists(self.file):
@@ -130,21 +132,22 @@ class IdleUserConfParser(IdleConfParser):
     def Save(self):
         """Update user configuration file.
 
-        Remove empty sections. If resulting config isn't empty, write the file
-        to disk. If config is empty, remove the file from disk if it exists.
+        If self not empty after removing empty sections, write the file
+        to disk. Otherwise, remove the file from disk if it exists.
 
         """
-        if not self.IsEmpty():
-            fname = self.file
-            try:
-                cfgFile = open(fname, 'w')
-            except OSError:
-                os.unlink(fname)
-                cfgFile = open(fname, 'w')
-            with cfgFile:
-                self.write(cfgFile)
-        else:
-            self.RemoveFile()
+        fname = self.file
+        if fname:
+            if not self.IsEmpty():
+                try:
+                    cfgFile = open(fname, 'w')
+                except OSError:
+                    os.unlink(fname)
+                    cfgFile = open(fname, 'w')
+                with cfgFile:
+                    self.write(cfgFile)
+            else:
+                self.RemoveFile()
 
 class IdleConf:
     """Hold config parsers for all idle config files in singleton instance.
@@ -157,23 +160,24 @@ class IdleConf:
         for config_type in self.config_types:
         (user home dir)/.idlerc/config-{config-type}.cfg
     """
-    def __init__(self):
-        self.config_types = ('main', 'extensions', 'highlight', 'keys')
+    def __init__(self, _utest=False):
+        self.config_types = ('main', 'highlight', 'keys', 'extensions')
         self.defaultCfg = {}
         self.userCfg = {}
         self.cfg = {}  # TODO use to select userCfg vs defaultCfg
-        self.CreateConfigHandlers()
-        self.LoadCfgFiles()
 
+        if not _utest:
+            self.CreateConfigHandlers()
+            self.LoadCfgFiles()
 
     def CreateConfigHandlers(self):
         "Populate default and user config parser dictionaries."
         #build idle install path
         if __name__ != '__main__': # we were imported
-            idleDir=os.path.dirname(__file__)
+            idleDir = os.path.dirname(__file__)
         else: # we were exec'ed (for testing only)
-            idleDir=os.path.abspath(sys.path[0])
-        userDir=self.GetUserCfgDir()
+            idleDir = os.path.abspath(sys.path[0])
+        self.userdir = userDir = self.GetUserCfgDir()
 
         defCfgFiles = {}
         usrCfgFiles = {}
@@ -213,7 +217,8 @@ class IdleConf:
             except OSError:
                 warn = ('\n Warning: unable to create user config directory\n' +
                         userDir + '\n Check path and permissions.\n Exiting!\n')
-                print(warn, file=sys.stderr)
+                if not idlelib.testing:
+                    print(warn, file=sys.stderr)
                 raise SystemExit
         # TODO continue without userDIr instead of exit
         return userDir
@@ -354,7 +359,8 @@ class IdleConf:
                 'stderr-foreground':'#000000',
                 'stderr-background':'#ffffff',
                 'console-foreground':'#000000',
-                'console-background':'#ffffff' }
+                'console-background':'#ffffff',
+                }
         for element in theme:
             if not cfgParser.has_option(themeName, element):
                 # Print warning that will return a default color
@@ -438,6 +444,11 @@ class IdleConf:
         for extn in userExtns:
             if extn not in extns: #user has added own extension
                 extns.append(extn)
+        for extn in ('AutoComplete','CodeContext',
+                     'FormatParagraph','ParenMatch'):
+            extns.remove(extn)
+            # specific exclusions because we are storing config for mainlined old
+            # extensions in config-extensions.def for backward compatibility
         if active_only:
             activeExtns = []
             for extn in extns:
@@ -461,16 +472,7 @@ class IdleConf:
 
     def RemoveKeyBindNames(self, extnNameList):
         "Return extnNameList with keybinding section names removed."
-        # TODO Easier to return filtered copy with list comp
-        names = extnNameList
-        kbNameIndicies = []
-        for name in names:
-            if name.endswith(('_bindings', '_cfgBindings')):
-                kbNameIndicies.append(names.index(name))
-        kbNameIndicies.sort(reverse=True)
-        for index in kbNameIndicies: #delete each keybinding section name
-            del(names[index])
-        return names
+        return [n for n in extnNameList if not n.endswith(('_bindings', '_cfgBindings'))]
 
     def GetExtnNameForEvent(self, virtualEvent):
         """Return the name of the extension binding virtualEvent, or None.
@@ -598,7 +600,12 @@ class IdleConf:
         return ('<<'+virtualEvent+'>>') in self.GetCoreKeys()
 
 # TODO make keyBindins a file or class attribute used for test above
-# and copied in function below
+# and copied in function below.
+
+    former_extension_events = {  #  Those with user-configurable keys.
+        '<<force-open-completions>>', '<<expand-word>>',
+        '<<force-open-calltip>>', '<<flash-paren>>', '<<format-paragraph>>',
+         '<<run-module>>', '<<check-module>>', '<<zoom-height>>'}
 
     def GetCoreKeys(self, keySetName=None):
         """Return dict of core virtual-key keybindings for keySetName.
@@ -658,8 +665,17 @@ class IdleConf:
             '<<toggle-tabs>>': ['<Alt-Key-t>'],
             '<<change-indentwidth>>': ['<Alt-Key-u>'],
             '<<del-word-left>>': ['<Control-Key-BackSpace>'],
-            '<<del-word-right>>': ['<Control-Key-Delete>']
+            '<<del-word-right>>': ['<Control-Key-Delete>'],
+            '<<force-open-completions>>': ['<Control-Key-space>'],
+            '<<expand-word>>': ['<Alt-Key-slash>'],
+            '<<force-open-calltip>>': ['<Control-Key-backslash>'],
+            '<<flash-paren>>': ['<Control-Key-0>'],
+            '<<format-paragraph>>': ['<Alt-Key-q>'],
+            '<<run-module>>': ['<Key-F5>'],
+            '<<check-module>>': ['<Alt-Key-x>'],
+            '<<zoom-height>>': ['<Alt-Key-2>'],
             }
+
         if keySetName:
             if not (self.userCfg['keys'].has_section(keySetName) or
                     self.defaultCfg['keys'].has_section(keySetName)):
@@ -674,7 +690,8 @@ class IdleConf:
                     binding = self.GetKeyBinding(keySetName, event)
                     if binding:
                         keyBindings[event] = binding
-                    else: #we are going to return a default, print warning
+                    # Otherwise return default in keyBindings.
+                    elif event not in self.former_extension_events:
                         warning = (
                             '\n Warning: config.py - IdleConf.GetCoreKeys -\n'
                             ' problem retrieving key binding for event %r\n'
@@ -766,7 +783,6 @@ class IdleConf:
 
 idleConf = IdleConf()
 
-
 _warned = set()
 def _warn(msg, *key):
     key = (msg,) + key
@@ -778,9 +794,106 @@ def _warn(msg, *key):
         _warned.add(key)
 
 
+class ConfigChanges(dict):
+    """Manage a user's proposed configuration option changes.
+
+    Names used across multiple methods:
+        page -- one of the 4 top-level dicts representing a
+                .idlerc/config-x.cfg file.
+        config_type -- name of a page.
+        section -- a section within a page/file.
+        option -- name of an option within a section.
+        value -- value for the option.
+
+    Methods
+        add_option: Add option and value to changes.
+        save_option: Save option and value to config parser.
+        save_all: Save all the changes to the config parser and file.
+        delete_section: If section exists,
+                        delete from changes, userCfg, and file.
+        clear: Clear all changes by clearing each page.
+    """
+    def __init__(self):
+        "Create a page for each configuration file"
+        self.pages = []  # List of unhashable dicts.
+        for config_type in idleConf.config_types:
+            self[config_type] = {}
+            self.pages.append(self[config_type])
+
+    def add_option(self, config_type, section, item, value):
+        "Add item/value pair for config_type and section."
+        page = self[config_type]
+        value = str(value)  # Make sure we use a string.
+        if section not in page:
+            page[section] = {}
+        page[section][item] = value
+
+    @staticmethod
+    def save_option(config_type, section, item, value):
+        """Return True if the configuration value was added or changed.
+
+        Helper for save_all.
+        """
+        if idleConf.defaultCfg[config_type].has_option(section, item):
+            if idleConf.defaultCfg[config_type].Get(section, item) == value:
+                # The setting equals a default setting, remove it from user cfg.
+                return idleConf.userCfg[config_type].RemoveOption(section, item)
+        # If we got here, set the option.
+        return idleConf.userCfg[config_type].SetOption(section, item, value)
+
+    def save_all(self):
+        """Save configuration changes to the user config file.
+
+        Clear self in preparation for additional changes.
+        Return changed for testing.
+        """
+        idleConf.userCfg['main'].Save()
+
+        changed = False
+        for config_type in self:
+            cfg_type_changed = False
+            page = self[config_type]
+            for section in page:
+                if section == 'HelpFiles':  # Remove it for replacement.
+                    idleConf.userCfg['main'].remove_section('HelpFiles')
+                    cfg_type_changed = True
+                for item, value in page[section].items():
+                    if self.save_option(config_type, section, item, value):
+                        cfg_type_changed = True
+            if cfg_type_changed:
+                idleConf.userCfg[config_type].Save()
+                changed = True
+        for config_type in ['keys', 'highlight']:
+            # Save these even if unchanged!
+            idleConf.userCfg[config_type].Save()
+        self.clear()
+        # ConfigDialog caller must add the following call
+        # self.save_all_changed_extensions()  # Uses a different mechanism.
+        return changed
+
+    def delete_section(self, config_type, section):
+        """Delete a section from self, userCfg, and file.
+
+        Used to delete custom themes and keysets.
+        """
+        if section in self[config_type]:
+            del self[config_type][section]
+        configpage = idleConf.userCfg[config_type]
+        configpage.remove_section(section)
+        configpage.Save()
+
+    def clear(self):
+        """Clear all 4 pages.
+
+        Called in save_all after saving to idleConf.
+        XXX Mark window *title* when there are changes; unmark here.
+        """
+        for page in self.pages:
+            page.clear()
+
+
 # TODO Revise test output, write expanded unittest
-#
-if __name__ == '__main__':
+def _dump():  # htest # (not really, but ignore in coverage)
     from zlib import crc32
     line, crc = 0, 0
 
@@ -790,10 +903,10 @@ if __name__ == '__main__':
         line += 1
         crc = crc32(txt.encode(encoding='utf-8'), crc)
         print(txt)
-        #print('***', line, crc, '***')  # uncomment for diagnosis
+        #print('***', line, crc, '***')  # Uncomment for diagnosis.
 
     def dumpCfg(cfg):
-        print('\n', cfg, '\n')  # has variable '0xnnnnnnnn' addresses
+        print('\n', cfg, '\n')  # Cfg has variable '0xnnnnnnnn' address.
         for key in sorted(cfg.keys()):
             sections = cfg[key].sections()
             sprint(key)
@@ -808,3 +921,9 @@ if __name__ == '__main__':
     dumpCfg(idleConf.defaultCfg)
     dumpCfg(idleConf.userCfg)
     print('\nlines = ', line, ', crc = ', crc, sep='')
+
+if __name__ == '__main__':
+    import unittest
+    unittest.main('idlelib.idle_test.test_config',
+                  verbosity=2, exit=False)
+    #_dump()
index 2602293..5556b76 100644 (file)
@@ -3,11 +3,16 @@ Dialog for building Tkinter accelerator key bindings
 """
 from tkinter import *
 from tkinter.ttk import Scrollbar
-import tkinter.messagebox as tkMessageBox
+from tkinter import messagebox
 import string
 import sys
 
+
 class GetKeysDialog(Toplevel):
+
+    # Dialog title for invalid key sequence
+    keyerror_title = 'Key Sequence Error'
+
     def __init__(self, parent, title, action, currentKeySequences,
                  _htest=False, _utest=False):
         """
@@ -54,6 +59,10 @@ class GetKeysDialog(Toplevel):
             self.deiconify() #geometry set, unhide
             self.wait_window()
 
+    def showerror(self, *args, **kwargs):
+        # Make testing easier.  Replace in #30751.
+        messagebox.showerror(*args, **kwargs)
+
     def CreateWidgets(self):
         frameMain = Frame(self,borderwidth=2,relief=SUNKEN)
         frameMain.pack(side=TOP,expand=TRUE,fill=BOTH)
@@ -186,7 +195,7 @@ class GetKeysDialog(Toplevel):
 
     def LoadFinalKeyList(self):
         #these tuples are also available for use in validity checks
-        self.functionKeys=('F1','F2','F2','F4','F5','F6','F7','F8','F9',
+        self.functionKeys=('F1','F2','F3','F4','F5','F6','F7','F8','F9',
                 'F10','F11','F12')
         self.alphanumKeys=tuple(string.ascii_lowercase+string.digits)
         self.punctuationKeys=tuple('~!@#%^&*()_-+={}[]|;:,.<>/?')
@@ -219,53 +228,71 @@ class GetKeysDialog(Toplevel):
         return key
 
     def OK(self, event=None):
-        if self.advanced or self.KeysOK():  # doesn't check advanced string yet
-            self.result=self.keyString.get()
-            self.destroy()
+        keys = self.keyString.get().strip()
+        if not keys:
+            self.showerror(title=self.keyerror_title, parent=self,
+                           message="No key specified.")
+            return
+        if (self.advanced or self.KeysOK(keys)) and self.bind_ok(keys):
+            self.result = keys
+        self.destroy()
 
     def Cancel(self, event=None):
         self.result=''
         self.destroy()
 
-    def KeysOK(self):
+    def KeysOK(self, keys):
         '''Validity check on user's 'basic' keybinding selection.
 
         Doesn't check the string produced by the advanced dialog because
         'modifiers' isn't set.
 
         '''
-        keys = self.keyString.get()
-        keys.strip()
         finalKey = self.listKeysFinal.get(ANCHOR)
         modifiers = self.GetModifiers()
-        # create a key sequence list for overlap check:
-        keySequence = keys.split()
         keysOK = False
-        title = 'Key Sequence Error'
-        if not keys:
-            tkMessageBox.showerror(title=title, parent=self,
-                                   message='No keys specified.')
-        elif not keys.endswith('>'):
-            tkMessageBox.showerror(title=title, parent=self,
-                                   message='Missing the final Key')
+        title = self.keyerror_title
+        key_sequences = [key for keylist in self.currentKeySequences
+                             for key in keylist]
+        if not keys.endswith('>'):
+            self.showerror(title, parent=self,
+                           message='Missing the final Key')
         elif (not modifiers
               and finalKey not in self.functionKeys + self.moveKeys):
-            tkMessageBox.showerror(title=title, parent=self,
-                                   message='No modifier key(s) specified.')
+            self.showerror(title=title, parent=self,
+                           message='No modifier key(s) specified.')
         elif (modifiers == ['Shift']) \
                  and (finalKey not in
                       self.functionKeys + self.moveKeys + ('Tab', 'Space')):
             msg = 'The shift modifier by itself may not be used with'\
                   ' this key symbol.'
-            tkMessageBox.showerror(title=title, parent=self, message=msg)
-        elif keySequence in self.currentKeySequences:
+            self.showerror(title=title, parent=self, message=msg)
+        elif keys in key_sequences:
             msg = 'This key combination is already in use.'
-            tkMessageBox.showerror(title=title, parent=self, message=msg)
+            self.showerror(title=title, parent=self, message=msg)
         else:
             keysOK = True
         return keysOK
 
+    def bind_ok(self, keys):
+        "Return True if Tcl accepts the new keys else show message."
+
+        try:
+            binding = self.bind(keys, lambda: None)
+        except TclError as err:
+            self.showerror(
+                    title=self.keyerror_title, parent=self,
+                    message=(f'The entered key sequence is not accepted.\n\n'
+                             f'Error: {err}'))
+            return False
+        else:
+            self.unbind(keys, binding)
+            return True
+
 
 if __name__ == '__main__':
+    import unittest
+    unittest.main('idlelib.idle_test.test_config_key', verbosity=2, exit=False)
+
     from idlelib.idle_test.htest import run
     run(GetKeysDialog)
index 8184582..0f530c6 100644 (file)
@@ -9,1217 +9,227 @@ Note that tab width in IDLE is currently fixed at eight due to Tk issues.
 Refer to comments in EditorWindow autoindent code for details.
 
 """
-from tkinter import *
-from tkinter.ttk import Scrollbar
+from tkinter import (Toplevel, Listbox, Text, Scale, Canvas,
+                     StringVar, BooleanVar, IntVar, TRUE, FALSE,
+                     TOP, BOTTOM, RIGHT, LEFT, SOLID, GROOVE, NORMAL, DISABLED,
+                     NONE, BOTH, X, Y, W, E, EW, NS, NSEW, NW,
+                     HORIZONTAL, VERTICAL, ANCHOR, ACTIVE, END)
+from tkinter.ttk import (Button, Checkbutton, Entry, Frame, Label, LabelFrame,
+                         OptionMenu, Notebook, Radiobutton, Scrollbar, Style)
 import tkinter.colorchooser as tkColorChooser
 import tkinter.font as tkFont
-import tkinter.messagebox as tkMessageBox
+from tkinter import messagebox
 
-from idlelib.config import idleConf
+from idlelib.config import idleConf, ConfigChanges
 from idlelib.config_key import GetKeysDialog
 from idlelib.dynoption import DynOptionMenu
 from idlelib import macosx
 from idlelib.query import SectionName, HelpSource
 from idlelib.tabbedpages import TabbedPageSet
 from idlelib.textview import view_text
+from idlelib.autocomplete import AutoComplete
+from idlelib.codecontext import CodeContext
+from idlelib.parenmatch import ParenMatch
+from idlelib.paragraph import FormatParagraph
+
+changes = ConfigChanges()
+# Reload changed options in the following classes.
+reloadables = (AutoComplete, CodeContext, ParenMatch, FormatParagraph)
+
 
 class ConfigDialog(Toplevel):
+    """Config dialog for IDLE.
+    """
 
     def __init__(self, parent, title='', _htest=False, _utest=False):
-        """
-        _htest - bool, change box location when running htest
-        _utest - bool, don't wait_window when running unittest
+        """Show the tabbed dialog for user configuration.
+
+        Args:
+            parent - parent of this dialog
+            title - string which is the title of this popup dialog
+            _htest - bool, change box location when running htest
+            _utest - bool, don't wait_window when running unittest
+
+        Note: Focus set on font page fontlist.
+
+        Methods:
+            create_widgets
+            cancel: Bound to DELETE_WINDOW protocol.
         """
         Toplevel.__init__(self, parent)
         self.parent = parent
         if _htest:
             parent.instance_dict = {}
-        self.wm_withdraw()
+        if not _utest:
+            self.withdraw()
 
         self.configure(borderwidth=5)
         self.title(title or 'IDLE Preferences')
-        self.geometry(
-                "+%d+%d" % (parent.winfo_rootx() + 20,
-                parent.winfo_rooty() + (30 if not _htest else 150)))
-        #Theme Elements. Each theme element key is its display name.
-        #The first value of the tuple is the sample area tag name.
-        #The second value is the display name list sort index.
-        self.themeElements={
-            'Normal Text': ('normal', '00'),
-            'Python Keywords': ('keyword', '01'),
-            'Python Definitions': ('definition', '02'),
-            'Python Builtins': ('builtin', '03'),
-            'Python Comments': ('comment', '04'),
-            'Python Strings': ('string', '05'),
-            'Selected Text': ('hilite', '06'),
-            'Found Text': ('hit', '07'),
-            'Cursor': ('cursor', '08'),
-            'Editor Breakpoint': ('break', '09'),
-            'Shell Normal Text': ('console', '10'),
-            'Shell Error Text': ('error', '11'),
-            'Shell Stdout Text': ('stdout', '12'),
-            'Shell Stderr Text': ('stderr', '13'),
-            }
-        self.ResetChangedItems() #load initial values in changed items dict
-        self.CreateWidgets()
+        x = parent.winfo_rootx() + 20
+        y = parent.winfo_rooty() + (30 if not _htest else 150)
+        self.geometry(f'+{x}+{y}')
+        # Each theme element key is its display name.
+        # The first value of the tuple is the sample area tag name.
+        # The second value is the display name list sort index.
+        self.create_widgets()
         self.resizable(height=FALSE, width=FALSE)
         self.transient(parent)
-        self.grab_set()
-        self.protocol("WM_DELETE_WINDOW", self.Cancel)
-        self.tabPages.focus_set()
-        #key bindings for this dialog
-        #self.bind('<Escape>', self.Cancel) #dismiss dialog, no save
-        #self.bind('<Alt-a>', self.Apply) #apply changes, save
-        #self.bind('<F1>', self.Help) #context help
-        self.LoadConfigs()
-        self.AttachVarCallbacks() #avoid callbacks during LoadConfigs
+        self.protocol("WM_DELETE_WINDOW", self.cancel)
+        self.fontpage.fontlist.focus_set()
+        # XXX Decide whether to keep or delete these key bindings.
+        # Key bindings for this dialog.
+        # self.bind('<Escape>', self.Cancel) #dismiss dialog, no save
+        # self.bind('<Alt-a>', self.Apply) #apply changes, save
+        # self.bind('<F1>', self.Help) #context help
+        # Attach callbacks after loading config to avoid calling them.
+        tracers.attach()
 
         if not _utest:
+            self.grab_set()
             self.wm_deiconify()
             self.wait_window()
 
-    def CreateWidgets(self):
-        self.tabPages = TabbedPageSet(self,
-                page_names=['Fonts/Tabs', 'Highlighting', 'Keys', 'General',
-                            'Extensions'])
-        self.tabPages.pack(side=TOP, expand=TRUE, fill=BOTH)
-        self.CreatePageFontTab()
-        self.CreatePageHighlight()
-        self.CreatePageKeys()
-        self.CreatePageGeneral()
-        self.CreatePageExtensions()
+    def create_widgets(self):
+        """Create and place widgets for tabbed dialog.
+
+        Widgets Bound to self:
+            note: Notebook
+            highpage: HighPage
+            fontpage: FontPage
+            keyspage: KeysPage
+            genpage: GenPage
+            extpage: self.create_page_extensions
+
+        Methods:
+            create_action_buttons
+            load_configs: Load pages except for extensions.
+            activate_config_changes: Tell editors to reload.
+        """
+        self.note = note = Notebook(self, width=450, height=450)
+        self.highpage = HighPage(note)
+        self.fontpage = FontPage(note, self.highpage)
+        self.keyspage = KeysPage(note)
+        self.genpage = GenPage(note)
+        self.extpage = self.create_page_extensions()
+        note.add(self.fontpage, text='Fonts/Tabs')
+        note.add(self.highpage, text='Highlights')
+        note.add(self.keyspage, text=' Keys ')
+        note.add(self.genpage, text=' General ')
+        note.add(self.extpage, text='Extensions')
+        note.enable_traversal()
+        note.pack(side=TOP, expand=TRUE, fill=BOTH)
         self.create_action_buttons().pack(side=BOTTOM)
 
     def create_action_buttons(self):
+        """Return frame of action buttons for dialog.
+
+        Methods:
+            ok
+            apply
+            cancel
+            help
+
+        Widget Structure:
+            outer: Frame
+                buttons: Frame
+                    (no assignment): Button (ok)
+                    (no assignment): Button (apply)
+                    (no assignment): Button (cancel)
+                    (no assignment): Button (help)
+                (no assignment): Frame
+        """
         if macosx.isAquaTk():
             # Changing the default padding on OSX results in unreadable
-            # text in the buttons
-            paddingArgs = {}
+            # text in the buttons.
+            padding_args = {}
         else:
-            paddingArgs = {'padx':6, 'pady':3}
-        outer = Frame(self, pady=2)
-        buttons = Frame(outer, pady=2)
+            padding_args = {'padding': (6, 3)}
+        outer = Frame(self, padding=2)
+        buttons = Frame(outer, padding=2)
         for txt, cmd in (
-            ('Ok', self.Ok),
-            ('Apply', self.Apply),
-            ('Cancel', self.Cancel),
-            ('Help', self.Help)):
+            ('Ok', self.ok),
+            ('Apply', self.apply),
+            ('Cancel', self.cancel),
+            ('Help', self.help)):
             Button(buttons, text=txt, command=cmd, takefocus=FALSE,
-                   **paddingArgs).pack(side=LEFT, padx=5)
-        # add space above buttons
+                   **padding_args).pack(side=LEFT, padx=5)
+        # Add space above buttons.
         Frame(outer, height=2, borderwidth=0).pack(side=TOP)
         buttons.pack(side=BOTTOM)
         return outer
 
-    def CreatePageFontTab(self):
-        parent = self.parent
-        self.fontSize = StringVar(parent)
-        self.fontBold = BooleanVar(parent)
-        self.fontName = StringVar(parent)
-        self.spaceNum = IntVar(parent)
-        self.editFont = tkFont.Font(parent, ('courier', 10, 'normal'))
-
-        ##widget creation
-        #body frame
-        frame = self.tabPages.pages['Fonts/Tabs'].frame
-        #body section frames
-        frameFont = LabelFrame(
-                frame, borderwidth=2, relief=GROOVE, text=' Base Editor Font ')
-        frameIndent = LabelFrame(
-                frame, borderwidth=2, relief=GROOVE, text=' Indentation Width ')
-        #frameFont
-        frameFontName = Frame(frameFont)
-        frameFontParam = Frame(frameFont)
-        labelFontNameTitle = Label(
-                frameFontName, justify=LEFT, text='Font Face :')
-        self.listFontName = Listbox(
-                frameFontName, height=5, takefocus=FALSE, exportselection=FALSE)
-        self.listFontName.bind(
-                '<ButtonRelease-1>', self.OnListFontButtonRelease)
-        scrollFont = Scrollbar(frameFontName)
-        scrollFont.config(command=self.listFontName.yview)
-        self.listFontName.config(yscrollcommand=scrollFont.set)
-        labelFontSizeTitle = Label(frameFontParam, text='Size :')
-        self.optMenuFontSize = DynOptionMenu(
-                frameFontParam, self.fontSize, None, command=self.SetFontSample)
-        checkFontBold = Checkbutton(
-                frameFontParam, variable=self.fontBold, onvalue=1,
-                offvalue=0, text='Bold', command=self.SetFontSample)
-        frameFontSample = Frame(frameFont, relief=SOLID, borderwidth=1)
-        self.labelFontSample = Label(
-                frameFontSample, justify=LEFT, font=self.editFont,
-                text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]')
-        #frameIndent
-        frameIndentSize = Frame(frameIndent)
-        labelSpaceNumTitle = Label(
-                frameIndentSize, justify=LEFT,
-                text='Python Standard: 4 Spaces!')
-        self.scaleSpaceNum = Scale(
-                frameIndentSize, variable=self.spaceNum,
-                orient='horizontal', tickinterval=2, from_=2, to=16)
-
-        #widget packing
-        #body
-        frameFont.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
-        frameIndent.pack(side=LEFT, padx=5, pady=5, fill=Y)
-        #frameFont
-        frameFontName.pack(side=TOP, padx=5, pady=5, fill=X)
-        frameFontParam.pack(side=TOP, padx=5, pady=5, fill=X)
-        labelFontNameTitle.pack(side=TOP, anchor=W)
-        self.listFontName.pack(side=LEFT, expand=TRUE, fill=X)
-        scrollFont.pack(side=LEFT, fill=Y)
-        labelFontSizeTitle.pack(side=LEFT, anchor=W)
-        self.optMenuFontSize.pack(side=LEFT, anchor=W)
-        checkFontBold.pack(side=LEFT, anchor=W, padx=20)
-        frameFontSample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
-        self.labelFontSample.pack(expand=TRUE, fill=BOTH)
-        #frameIndent
-        frameIndentSize.pack(side=TOP, fill=X)
-        labelSpaceNumTitle.pack(side=TOP, anchor=W, padx=5)
-        self.scaleSpaceNum.pack(side=TOP, padx=5, fill=X)
-        return frame
-
-    def CreatePageHighlight(self):
-        parent = self.parent
-        self.builtinTheme = StringVar(parent)
-        self.customTheme = StringVar(parent)
-        self.fgHilite = BooleanVar(parent)
-        self.colour = StringVar(parent)
-        self.fontName = StringVar(parent)
-        self.themeIsBuiltin = BooleanVar(parent)
-        self.highlightTarget = StringVar(parent)
-
-        ##widget creation
-        #body frame
-        frame = self.tabPages.pages['Highlighting'].frame
-        #body section frames
-        frameCustom = LabelFrame(frame, borderwidth=2, relief=GROOVE,
-                                 text=' Custom Highlighting ')
-        frameTheme = LabelFrame(frame, borderwidth=2, relief=GROOVE,
-                                text=' Highlighting Theme ')
-        #frameCustom
-        self.textHighlightSample=Text(
-                frameCustom, relief=SOLID, borderwidth=1,
-                font=('courier', 12, ''), cursor='hand2', width=21, height=11,
-                takefocus=FALSE, highlightthickness=0, wrap=NONE)
-        text=self.textHighlightSample
-        text.bind('<Double-Button-1>', lambda e: 'break')
-        text.bind('<B1-Motion>', lambda e: 'break')
-        textAndTags=(
-            ('#you can click here', 'comment'), ('\n', 'normal'),
-            ('#to choose items', 'comment'), ('\n', 'normal'),
-            ('def', 'keyword'), (' ', 'normal'),
-            ('func', 'definition'), ('(param):\n  ', 'normal'),
-            ('"""string"""', 'string'), ('\n  var0 = ', 'normal'),
-            ("'string'", 'string'), ('\n  var1 = ', 'normal'),
-            ("'selected'", 'hilite'), ('\n  var2 = ', 'normal'),
-            ("'found'", 'hit'), ('\n  var3 = ', 'normal'),
-            ('list', 'builtin'), ('(', 'normal'),
-            ('None', 'keyword'), (')\n', 'normal'),
-            ('  breakpoint("line")', 'break'), ('\n\n', 'normal'),
-            (' error ', 'error'), (' ', 'normal'),
-            ('cursor |', 'cursor'), ('\n ', 'normal'),
-            ('shell', 'console'), (' ', 'normal'),
-            ('stdout', 'stdout'), (' ', 'normal'),
-            ('stderr', 'stderr'), ('\n', 'normal'))
-        for txTa in textAndTags:
-            text.insert(END, txTa[0], txTa[1])
-        for element in self.themeElements:
-            def tem(event, elem=element):
-                event.widget.winfo_toplevel().highlightTarget.set(elem)
-            text.tag_bind(
-                    self.themeElements[element][0], '<ButtonPress-1>', tem)
-        text.config(state=DISABLED)
-        self.frameColourSet = Frame(frameCustom, relief=SOLID, borderwidth=1)
-        frameFgBg = Frame(frameCustom)
-        buttonSetColour = Button(
-                self.frameColourSet, text='Choose Colour for :',
-                command=self.GetColour, highlightthickness=0)
-        self.optMenuHighlightTarget = DynOptionMenu(
-                self.frameColourSet, self.highlightTarget, None,
-                highlightthickness=0) #, command=self.SetHighlightTargetBinding
-        self.radioFg = Radiobutton(
-                frameFgBg, variable=self.fgHilite, value=1,
-                text='Foreground', command=self.SetColourSampleBinding)
-        self.radioBg=Radiobutton(
-                frameFgBg, variable=self.fgHilite, value=0,
-                text='Background', command=self.SetColourSampleBinding)
-        self.fgHilite.set(1)
-        buttonSaveCustomTheme = Button(
-                frameCustom, text='Save as New Custom Theme',
-                command=self.SaveAsNewTheme)
-        #frameTheme
-        labelTypeTitle = Label(frameTheme, text='Select : ')
-        self.radioThemeBuiltin = Radiobutton(
-                frameTheme, variable=self.themeIsBuiltin, value=1,
-                command=self.SetThemeType, text='a Built-in Theme')
-        self.radioThemeCustom = Radiobutton(
-                frameTheme, variable=self.themeIsBuiltin, value=0,
-                command=self.SetThemeType, text='a Custom Theme')
-        self.optMenuThemeBuiltin = DynOptionMenu(
-                frameTheme, self.builtinTheme, None, command=None)
-        self.optMenuThemeCustom=DynOptionMenu(
-                frameTheme, self.customTheme, None, command=None)
-        self.buttonDeleteCustomTheme=Button(
-                frameTheme, text='Delete Custom Theme',
-                command=self.DeleteCustomTheme)
-        self.new_custom_theme = Label(frameTheme, bd=2)
-
-        ##widget packing
-        #body
-        frameCustom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
-        frameTheme.pack(side=LEFT, padx=5, pady=5, fill=Y)
-        #frameCustom
-        self.frameColourSet.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X)
-        frameFgBg.pack(side=TOP, padx=5, pady=0)
-        self.textHighlightSample.pack(
-                side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
-        buttonSetColour.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4)
-        self.optMenuHighlightTarget.pack(
-                side=TOP, expand=TRUE, fill=X, padx=8, pady=3)
-        self.radioFg.pack(side=LEFT, anchor=E)
-        self.radioBg.pack(side=RIGHT, anchor=W)
-        buttonSaveCustomTheme.pack(side=BOTTOM, fill=X, padx=5, pady=5)
-        #frameTheme
-        labelTypeTitle.pack(side=TOP, anchor=W, padx=5, pady=5)
-        self.radioThemeBuiltin.pack(side=TOP, anchor=W, padx=5)
-        self.radioThemeCustom.pack(side=TOP, anchor=W, padx=5, pady=2)
-        self.optMenuThemeBuiltin.pack(side=TOP, fill=X, padx=5, pady=5)
-        self.optMenuThemeCustom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5)
-        self.buttonDeleteCustomTheme.pack(side=TOP, fill=X, padx=5, pady=5)
-        self.new_custom_theme.pack(side=TOP, fill=X, pady=5)
-        return frame
-
-    def CreatePageKeys(self):
-        parent = self.parent
-        self.bindingTarget = StringVar(parent)
-        self.builtinKeys = StringVar(parent)
-        self.customKeys = StringVar(parent)
-        self.keysAreBuiltin = BooleanVar(parent)
-        self.keyBinding = StringVar(parent)
-
-        ##widget creation
-        #body frame
-        frame = self.tabPages.pages['Keys'].frame
-        #body section frames
-        frameCustom = LabelFrame(
-                frame, borderwidth=2, relief=GROOVE,
-                text=' Custom Key Bindings ')
-        frameKeySets = LabelFrame(
-                frame, borderwidth=2, relief=GROOVE, text=' Key Set ')
-        #frameCustom
-        frameTarget = Frame(frameCustom)
-        labelTargetTitle = Label(frameTarget, text='Action - Key(s)')
-        scrollTargetY = Scrollbar(frameTarget)
-        scrollTargetX = Scrollbar(frameTarget, orient=HORIZONTAL)
-        self.listBindings = Listbox(
-                frameTarget, takefocus=FALSE, exportselection=FALSE)
-        self.listBindings.bind('<ButtonRelease-1>', self.KeyBindingSelected)
-        scrollTargetY.config(command=self.listBindings.yview)
-        scrollTargetX.config(command=self.listBindings.xview)
-        self.listBindings.config(yscrollcommand=scrollTargetY.set)
-        self.listBindings.config(xscrollcommand=scrollTargetX.set)
-        self.buttonNewKeys = Button(
-                frameCustom, text='Get New Keys for Selection',
-                command=self.GetNewKeys, state=DISABLED)
-        #frameKeySets
-        frames = [Frame(frameKeySets, padx=2, pady=2, borderwidth=0)
-                  for i in range(2)]
-        self.radioKeysBuiltin = Radiobutton(
-                frames[0], variable=self.keysAreBuiltin, value=1,
-                command=self.SetKeysType, text='Use a Built-in Key Set')
-        self.radioKeysCustom = Radiobutton(
-                frames[0], variable=self.keysAreBuiltin,  value=0,
-                command=self.SetKeysType, text='Use a Custom Key Set')
-        self.optMenuKeysBuiltin = DynOptionMenu(
-                frames[0], self.builtinKeys, None, command=None)
-        self.optMenuKeysCustom = DynOptionMenu(
-                frames[0], self.customKeys, None, command=None)
-        self.buttonDeleteCustomKeys = Button(
-                frames[1], text='Delete Custom Key Set',
-                command=self.DeleteCustomKeys)
-        buttonSaveCustomKeys = Button(
-                frames[1], text='Save as New Custom Key Set',
-                command=self.SaveAsNewKeySet)
-        self.new_custom_keys = Label(frames[0], bd=2)
-
-        ##widget packing
-        #body
-        frameCustom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH)
-        frameKeySets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH)
-        #frameCustom
-        self.buttonNewKeys.pack(side=BOTTOM, fill=X, padx=5, pady=5)
-        frameTarget.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
-        #frame target
-        frameTarget.columnconfigure(0, weight=1)
-        frameTarget.rowconfigure(1, weight=1)
-        labelTargetTitle.grid(row=0, column=0, columnspan=2, sticky=W)
-        self.listBindings.grid(row=1, column=0, sticky=NSEW)
-        scrollTargetY.grid(row=1, column=1, sticky=NS)
-        scrollTargetX.grid(row=2, column=0, sticky=EW)
-        #frameKeySets
-        self.radioKeysBuiltin.grid(row=0, column=0, sticky=W+NS)
-        self.radioKeysCustom.grid(row=1, column=0, sticky=W+NS)
-        self.optMenuKeysBuiltin.grid(row=0, column=1, sticky=NSEW)
-        self.optMenuKeysCustom.grid(row=1, column=1, sticky=NSEW)
-        self.new_custom_keys.grid(row=0, column=2, sticky=NSEW, padx=5, pady=5)
-        self.buttonDeleteCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2)
-        buttonSaveCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2)
-        frames[0].pack(side=TOP, fill=BOTH, expand=True)
-        frames[1].pack(side=TOP, fill=X, expand=True, pady=2)
-        return frame
-
-    def CreatePageGeneral(self):
-        parent = self.parent
-        self.winWidth = StringVar(parent)
-        self.winHeight = StringVar(parent)
-        self.startupEdit = IntVar(parent)
-        self.autoSave = IntVar(parent)
-        self.encoding = StringVar(parent)
-        self.userHelpBrowser = BooleanVar(parent)
-        self.helpBrowser = StringVar(parent)
-
-        #widget creation
-        #body
-        frame = self.tabPages.pages['General'].frame
-        #body section frames
-        frameRun = LabelFrame(frame, borderwidth=2, relief=GROOVE,
-                              text=' Startup Preferences ')
-        frameSave = LabelFrame(frame, borderwidth=2, relief=GROOVE,
-                               text=' Autosave Preferences ')
-        frameWinSize = Frame(frame, borderwidth=2, relief=GROOVE)
-        frameHelp = LabelFrame(frame, borderwidth=2, relief=GROOVE,
-                               text=' Additional Help Sources ')
-        #frameRun
-        labelRunChoiceTitle = Label(frameRun, text='At Startup')
-        self.radioStartupEdit = Radiobutton(
-                frameRun, variable=self.startupEdit, value=1,
-                text="Open Edit Window")
-        self.radioStartupShell = Radiobutton(
-                frameRun, variable=self.startupEdit, value=0,
-                text='Open Shell Window')
-        #frameSave
-        labelRunSaveTitle = Label(frameSave, text='At Start of Run (F5)  ')
-        self.radioSaveAsk = Radiobutton(
-                frameSave, variable=self.autoSave, value=0,
-                text="Prompt to Save")
-        self.radioSaveAuto = Radiobutton(
-                frameSave, variable=self.autoSave, value=1,
-                text='No Prompt')
-        #frameWinSize
-        labelWinSizeTitle = Label(
-                frameWinSize, text='Initial Window Size  (in characters)')
-        labelWinWidthTitle = Label(frameWinSize, text='Width')
-        self.entryWinWidth = Entry(
-                frameWinSize, textvariable=self.winWidth, width=3)
-        labelWinHeightTitle = Label(frameWinSize, text='Height')
-        self.entryWinHeight = Entry(
-                frameWinSize, textvariable=self.winHeight, width=3)
-        #frameHelp
-        frameHelpList = Frame(frameHelp)
-        frameHelpListButtons = Frame(frameHelpList)
-        scrollHelpList = Scrollbar(frameHelpList)
-        self.listHelp = Listbox(
-                frameHelpList, height=5, takefocus=FALSE,
-                exportselection=FALSE)
-        scrollHelpList.config(command=self.listHelp.yview)
-        self.listHelp.config(yscrollcommand=scrollHelpList.set)
-        self.listHelp.bind('<ButtonRelease-1>', self.HelpSourceSelected)
-        self.buttonHelpListEdit = Button(
-                frameHelpListButtons, text='Edit', state=DISABLED,
-                width=8, command=self.HelpListItemEdit)
-        self.buttonHelpListAdd = Button(
-                frameHelpListButtons, text='Add',
-                width=8, command=self.HelpListItemAdd)
-        self.buttonHelpListRemove = Button(
-                frameHelpListButtons, text='Remove', state=DISABLED,
-                width=8, command=self.HelpListItemRemove)
-
-        #widget packing
-        #body
-        frameRun.pack(side=TOP, padx=5, pady=5, fill=X)
-        frameSave.pack(side=TOP, padx=5, pady=5, fill=X)
-        frameWinSize.pack(side=TOP, padx=5, pady=5, fill=X)
-        frameHelp.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
-        #frameRun
-        labelRunChoiceTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
-        self.radioStartupShell.pack(side=RIGHT, anchor=W, padx=5, pady=5)
-        self.radioStartupEdit.pack(side=RIGHT, anchor=W, padx=5, pady=5)
-        #frameSave
-        labelRunSaveTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
-        self.radioSaveAuto.pack(side=RIGHT, anchor=W, padx=5, pady=5)
-        self.radioSaveAsk.pack(side=RIGHT, anchor=W, padx=5, pady=5)
-        #frameWinSize
-        labelWinSizeTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
-        self.entryWinHeight.pack(side=RIGHT, anchor=E, padx=10, pady=5)
-        labelWinHeightTitle.pack(side=RIGHT, anchor=E, pady=5)
-        self.entryWinWidth.pack(side=RIGHT, anchor=E, padx=10, pady=5)
-        labelWinWidthTitle.pack(side=RIGHT, anchor=E, pady=5)
-        #frameHelp
-        frameHelpListButtons.pack(side=RIGHT, padx=5, pady=5, fill=Y)
-        frameHelpList.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
-        scrollHelpList.pack(side=RIGHT, anchor=W, fill=Y)
-        self.listHelp.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH)
-        self.buttonHelpListEdit.pack(side=TOP, anchor=W, pady=5)
-        self.buttonHelpListAdd.pack(side=TOP, anchor=W)
-        self.buttonHelpListRemove.pack(side=TOP, anchor=W, pady=5)
-        return frame
-
-    def AttachVarCallbacks(self):
-        self.fontSize.trace_add('write', self.VarChanged_font)
-        self.fontName.trace_add('write', self.VarChanged_font)
-        self.fontBold.trace_add('write', self.VarChanged_font)
-        self.spaceNum.trace_add('write', self.VarChanged_spaceNum)
-        self.colour.trace_add('write', self.VarChanged_colour)
-        self.builtinTheme.trace_add('write', self.VarChanged_builtinTheme)
-        self.customTheme.trace_add('write', self.VarChanged_customTheme)
-        self.themeIsBuiltin.trace_add('write', self.VarChanged_themeIsBuiltin)
-        self.highlightTarget.trace_add('write', self.VarChanged_highlightTarget)
-        self.keyBinding.trace_add('write', self.VarChanged_keyBinding)
-        self.builtinKeys.trace_add('write', self.VarChanged_builtinKeys)
-        self.customKeys.trace_add('write', self.VarChanged_customKeys)
-        self.keysAreBuiltin.trace_add('write', self.VarChanged_keysAreBuiltin)
-        self.winWidth.trace_add('write', self.VarChanged_winWidth)
-        self.winHeight.trace_add('write', self.VarChanged_winHeight)
-        self.startupEdit.trace_add('write', self.VarChanged_startupEdit)
-        self.autoSave.trace_add('write', self.VarChanged_autoSave)
-        self.encoding.trace_add('write', self.VarChanged_encoding)
-
-    def remove_var_callbacks(self):
-        "Remove callbacks to prevent memory leaks."
-        for var in (
-                self.fontSize, self.fontName, self.fontBold,
-                self.spaceNum, self.colour, self.builtinTheme,
-                self.customTheme, self.themeIsBuiltin, self.highlightTarget,
-                self.keyBinding, self.builtinKeys, self.customKeys,
-                self.keysAreBuiltin, self.winWidth, self.winHeight,
-                self.startupEdit, self.autoSave, self.encoding,):
-            var.trace_remove('write', var.trace_info()[0][1])
-
-    def VarChanged_font(self, *params):
-        '''When one font attribute changes, save them all, as they are
-        not independent from each other. In particular, when we are
-        overriding the default font, we need to write out everything.
-        '''
-        value = self.fontName.get()
-        self.AddChangedItem('main', 'EditorWindow', 'font', value)
-        value = self.fontSize.get()
-        self.AddChangedItem('main', 'EditorWindow', 'font-size', value)
-        value = self.fontBold.get()
-        self.AddChangedItem('main', 'EditorWindow', 'font-bold', value)
-
-    def VarChanged_spaceNum(self, *params):
-        value = self.spaceNum.get()
-        self.AddChangedItem('main', 'Indent', 'num-spaces', value)
-
-    def VarChanged_colour(self, *params):
-        self.OnNewColourSet()
-
-    def VarChanged_builtinTheme(self, *params):
-        oldthemes = ('IDLE Classic', 'IDLE New')
-        value = self.builtinTheme.get()
-        if value not in oldthemes:
-            if idleConf.GetOption('main', 'Theme', 'name') not in oldthemes:
-                self.AddChangedItem('main', 'Theme', 'name', oldthemes[0])
-            self.AddChangedItem('main', 'Theme', 'name2', value)
-            self.new_custom_theme.config(text='New theme, see Help',
-                                         fg='#500000')
-        else:
-            self.AddChangedItem('main', 'Theme', 'name', value)
-            self.AddChangedItem('main', 'Theme', 'name2', '')
-            self.new_custom_theme.config(text='', fg='black')
-        self.PaintThemeSample()
-
-    def VarChanged_customTheme(self, *params):
-        value = self.customTheme.get()
-        if value != '- no custom themes -':
-            self.AddChangedItem('main', 'Theme', 'name', value)
-            self.PaintThemeSample()
-
-    def VarChanged_themeIsBuiltin(self, *params):
-        value = self.themeIsBuiltin.get()
-        self.AddChangedItem('main', 'Theme', 'default', value)
-        if value:
-            self.VarChanged_builtinTheme()
-        else:
-            self.VarChanged_customTheme()
-
-    def VarChanged_highlightTarget(self, *params):
-        self.SetHighlightTarget()
+    def ok(self):
+        """Apply config changes, then dismiss dialog.
 
-    def VarChanged_keyBinding(self, *params):
-        value = self.keyBinding.get()
-        keySet = self.customKeys.get()
-        event = self.listBindings.get(ANCHOR).split()[0]
-        if idleConf.IsCoreBinding(event):
-            #this is a core keybinding
-            self.AddChangedItem('keys', keySet, event, value)
-        else: #this is an extension key binding
-            extName = idleConf.GetExtnNameForEvent(event)
-            extKeybindSection = extName + '_cfgBindings'
-            self.AddChangedItem('extensions', extKeybindSection, event, value)
-
-    def VarChanged_builtinKeys(self, *params):
-        oldkeys = (
-            'IDLE Classic Windows',
-            'IDLE Classic Unix',
-            'IDLE Classic Mac',
-            'IDLE Classic OSX',
-        )
-        value = self.builtinKeys.get()
-        if value not in oldkeys:
-            if idleConf.GetOption('main', 'Keys', 'name') not in oldkeys:
-                self.AddChangedItem('main', 'Keys', 'name', oldkeys[0])
-            self.AddChangedItem('main', 'Keys', 'name2', value)
-            self.new_custom_keys.config(text='New key set, see Help',
-                                        fg='#500000')
-        else:
-            self.AddChangedItem('main', 'Keys', 'name', value)
-            self.AddChangedItem('main', 'Keys', 'name2', '')
-            self.new_custom_keys.config(text='', fg='black')
-        self.LoadKeysList(value)
+        Methods:
+            apply
+            destroy: inherited
+        """
+        self.apply()
+        self.destroy()
 
-    def VarChanged_customKeys(self, *params):
-        value = self.customKeys.get()
-        if value != '- no custom keys -':
-            self.AddChangedItem('main', 'Keys', 'name', value)
-            self.LoadKeysList(value)
+    def apply(self):
+        """Apply config changes and leave dialog open.
 
-    def VarChanged_keysAreBuiltin(self, *params):
-        value = self.keysAreBuiltin.get()
-        self.AddChangedItem('main', 'Keys', 'default', value)
-        if value:
-            self.VarChanged_builtinKeys()
-        else:
-            self.VarChanged_customKeys()
-
-    def VarChanged_winWidth(self, *params):
-        value = self.winWidth.get()
-        self.AddChangedItem('main', 'EditorWindow', 'width', value)
-
-    def VarChanged_winHeight(self, *params):
-        value = self.winHeight.get()
-        self.AddChangedItem('main', 'EditorWindow', 'height', value)
-
-    def VarChanged_startupEdit(self, *params):
-        value = self.startupEdit.get()
-        self.AddChangedItem('main', 'General', 'editor-on-startup', value)
-
-    def VarChanged_autoSave(self, *params):
-        value = self.autoSave.get()
-        self.AddChangedItem('main', 'General', 'autosave', value)
-
-    def VarChanged_encoding(self, *params):
-        value = self.encoding.get()
-        self.AddChangedItem('main', 'EditorWindow', 'encoding', value)
-
-    def ResetChangedItems(self):
-        #When any config item is changed in this dialog, an entry
-        #should be made in the relevant section (config type) of this
-        #dictionary. The key should be the config file section name and the
-        #value a dictionary, whose key:value pairs are item=value pairs for
-        #that config file section.
-        self.changedItems = {'main':{}, 'highlight':{}, 'keys':{},
-                             'extensions':{}}
-
-    def AddChangedItem(self, typ, section, item, value):
-        value = str(value) #make sure we use a string
-        if section not in self.changedItems[typ]:
-            self.changedItems[typ][section] = {}
-        self.changedItems[typ][section][item] = value
-
-    def GetDefaultItems(self):
-        dItems={'main':{}, 'highlight':{}, 'keys':{}, 'extensions':{}}
-        for configType in dItems:
-            sections = idleConf.GetSectionList('default', configType)
-            for section in sections:
-                dItems[configType][section] = {}
-                options = idleConf.defaultCfg[configType].GetOptionList(section)
-                for option in options:
-                    dItems[configType][section][option] = (
-                            idleConf.defaultCfg[configType].Get(section, option))
-        return dItems
-
-    def SetThemeType(self):
-        if self.themeIsBuiltin.get():
-            self.optMenuThemeBuiltin.config(state=NORMAL)
-            self.optMenuThemeCustom.config(state=DISABLED)
-            self.buttonDeleteCustomTheme.config(state=DISABLED)
-        else:
-            self.optMenuThemeBuiltin.config(state=DISABLED)
-            self.radioThemeCustom.config(state=NORMAL)
-            self.optMenuThemeCustom.config(state=NORMAL)
-            self.buttonDeleteCustomTheme.config(state=NORMAL)
-
-    def SetKeysType(self):
-        if self.keysAreBuiltin.get():
-            self.optMenuKeysBuiltin.config(state=NORMAL)
-            self.optMenuKeysCustom.config(state=DISABLED)
-            self.buttonDeleteCustomKeys.config(state=DISABLED)
-        else:
-            self.optMenuKeysBuiltin.config(state=DISABLED)
-            self.radioKeysCustom.config(state=NORMAL)
-            self.optMenuKeysCustom.config(state=NORMAL)
-            self.buttonDeleteCustomKeys.config(state=NORMAL)
-
-    def GetNewKeys(self):
-        listIndex = self.listBindings.index(ANCHOR)
-        binding = self.listBindings.get(listIndex)
-        bindName = binding.split()[0] #first part, up to first space
-        if self.keysAreBuiltin.get():
-            currentKeySetName = self.builtinKeys.get()
-        else:
-            currentKeySetName = self.customKeys.get()
-        currentBindings = idleConf.GetCurrentKeySet()
-        if currentKeySetName in self.changedItems['keys']: #unsaved changes
-            keySetChanges = self.changedItems['keys'][currentKeySetName]
-            for event in keySetChanges:
-                currentBindings[event] = keySetChanges[event].split()
-        currentKeySequences = list(currentBindings.values())
-        newKeys = GetKeysDialog(self, 'Get New Keys', bindName,
-                currentKeySequences).result
-        if newKeys: #new keys were specified
-            if self.keysAreBuiltin.get(): #current key set is a built-in
-                message = ('Your changes will be saved as a new Custom Key Set.'
-                           ' Enter a name for your new Custom Key Set below.')
-                newKeySet = self.GetNewKeysName(message)
-                if not newKeySet: #user cancelled custom key set creation
-                    self.listBindings.select_set(listIndex)
-                    self.listBindings.select_anchor(listIndex)
-                    return
-                else: #create new custom key set based on previously active key set
-                    self.CreateNewKeySet(newKeySet)
-            self.listBindings.delete(listIndex)
-            self.listBindings.insert(listIndex, bindName+' - '+newKeys)
-            self.listBindings.select_set(listIndex)
-            self.listBindings.select_anchor(listIndex)
-            self.keyBinding.set(newKeys)
-        else:
-            self.listBindings.select_set(listIndex)
-            self.listBindings.select_anchor(listIndex)
+        Methods:
+            deactivate_current_config
+            save_all_changed_extensions
+            activate_config_changes
+        """
+        self.deactivate_current_config()
+        changes.save_all()
+        self.save_all_changed_extensions()
+        self.activate_config_changes()
 
-    def GetNewKeysName(self, message):
-        usedNames = (idleConf.GetSectionList('user', 'keys') +
-                idleConf.GetSectionList('default', 'keys'))
-        newKeySet = SectionName(
-                self, 'New Custom Key Set', message, usedNames).result
-        return newKeySet
-
-    def SaveAsNewKeySet(self):
-        newKeysName = self.GetNewKeysName('New Key Set Name:')
-        if newKeysName:
-            self.CreateNewKeySet(newKeysName)
-
-    def KeyBindingSelected(self, event):
-        self.buttonNewKeys.config(state=NORMAL)
-
-    def CreateNewKeySet(self, newKeySetName):
-        #creates new custom key set based on the previously active key set,
-        #and makes the new key set active
-        if self.keysAreBuiltin.get():
-            prevKeySetName = self.builtinKeys.get()
-        else:
-            prevKeySetName = self.customKeys.get()
-        prevKeys = idleConf.GetCoreKeys(prevKeySetName)
-        newKeys = {}
-        for event in prevKeys: #add key set to changed items
-            eventName = event[2:-2] #trim off the angle brackets
-            binding = ' '.join(prevKeys[event])
-            newKeys[eventName] = binding
-        #handle any unsaved changes to prev key set
-        if prevKeySetName in self.changedItems['keys']:
-            keySetChanges = self.changedItems['keys'][prevKeySetName]
-            for event in keySetChanges:
-                newKeys[event] = keySetChanges[event]
-        #save the new theme
-        self.SaveNewKeySet(newKeySetName, newKeys)
-        #change gui over to the new key set
-        customKeyList = idleConf.GetSectionList('user', 'keys')
-        customKeyList.sort()
-        self.optMenuKeysCustom.SetMenu(customKeyList, newKeySetName)
-        self.keysAreBuiltin.set(0)
-        self.SetKeysType()
-
-    def LoadKeysList(self, keySetName):
-        reselect = 0
-        newKeySet = 0
-        if self.listBindings.curselection():
-            reselect = 1
-            listIndex = self.listBindings.index(ANCHOR)
-        keySet = idleConf.GetKeySet(keySetName)
-        bindNames = list(keySet.keys())
-        bindNames.sort()
-        self.listBindings.delete(0, END)
-        for bindName in bindNames:
-            key = ' '.join(keySet[bindName]) #make key(s) into a string
-            bindName = bindName[2:-2] #trim off the angle brackets
-            if keySetName in self.changedItems['keys']:
-                #handle any unsaved changes to this key set
-                if bindName in self.changedItems['keys'][keySetName]:
-                    key = self.changedItems['keys'][keySetName][bindName]
-            self.listBindings.insert(END, bindName+' - '+key)
-        if reselect:
-            self.listBindings.see(listIndex)
-            self.listBindings.select_set(listIndex)
-            self.listBindings.select_anchor(listIndex)
+    def cancel(self):
+        """Dismiss config dialog.
 
-    def DeleteCustomKeys(self):
-        keySetName=self.customKeys.get()
-        delmsg = 'Are you sure you wish to delete the key set %r ?'
-        if not tkMessageBox.askyesno(
-                'Delete Key Set',  delmsg % keySetName, parent=self):
-            return
-        self.DeactivateCurrentConfig()
-        #remove key set from config
-        idleConf.userCfg['keys'].remove_section(keySetName)
-        if keySetName in self.changedItems['keys']:
-            del(self.changedItems['keys'][keySetName])
-        #write changes
-        idleConf.userCfg['keys'].Save()
-        #reload user key set list
-        itemList = idleConf.GetSectionList('user', 'keys')
-        itemList.sort()
-        if not itemList:
-            self.radioKeysCustom.config(state=DISABLED)
-            self.optMenuKeysCustom.SetMenu(itemList, '- no custom keys -')
-        else:
-            self.optMenuKeysCustom.SetMenu(itemList, itemList[0])
-        #revert to default key set
-        self.keysAreBuiltin.set(idleConf.defaultCfg['main']
-                                .Get('Keys', 'default'))
-        self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')
-                             or idleConf.default_keys())
-        #user can't back out of these changes, they must be applied now
-        self.SaveAllChangedConfigs()
-        self.ActivateConfigChanges()
-        self.SetKeysType()
+        Methods:
+            destroy: inherited
+        """
+        self.destroy()
 
-    def DeleteCustomTheme(self):
-        themeName = self.customTheme.get()
-        delmsg = 'Are you sure you wish to delete the theme %r ?'
-        if not tkMessageBox.askyesno(
-                'Delete Theme',  delmsg % themeName, parent=self):
-            return
-        self.DeactivateCurrentConfig()
-        #remove theme from config
-        idleConf.userCfg['highlight'].remove_section(themeName)
-        if themeName in self.changedItems['highlight']:
-            del(self.changedItems['highlight'][themeName])
-        #write changes
-        idleConf.userCfg['highlight'].Save()
-        #reload user theme list
-        itemList = idleConf.GetSectionList('user', 'highlight')
-        itemList.sort()
-        if not itemList:
-            self.radioThemeCustom.config(state=DISABLED)
-            self.optMenuThemeCustom.SetMenu(itemList, '- no custom themes -')
-        else:
-            self.optMenuThemeCustom.SetMenu(itemList, itemList[0])
-        #revert to default theme
-        self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme', 'default'))
-        self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme', 'name'))
-        #user can't back out of these changes, they must be applied now
-        self.SaveAllChangedConfigs()
-        self.ActivateConfigChanges()
-        self.SetThemeType()
-
-    def GetColour(self):
-        target = self.highlightTarget.get()
-        prevColour = self.frameColourSet.cget('bg')
-        rgbTuplet, colourString = tkColorChooser.askcolor(
-                parent=self, title='Pick new colour for : '+target,
-                initialcolor=prevColour)
-        if colourString and (colourString != prevColour):
-            #user didn't cancel, and they chose a new colour
-            if self.themeIsBuiltin.get():  #current theme is a built-in
-                message = ('Your changes will be saved as a new Custom Theme. '
-                           'Enter a name for your new Custom Theme below.')
-                newTheme = self.GetNewThemeName(message)
-                if not newTheme:  #user cancelled custom theme creation
-                    return
-                else:  #create new custom theme based on previously active theme
-                    self.CreateNewTheme(newTheme)
-                    self.colour.set(colourString)
-            else:  #current theme is user defined
-                self.colour.set(colourString)
-
-    def OnNewColourSet(self):
-        newColour=self.colour.get()
-        self.frameColourSet.config(bg=newColour)  #set sample
-        plane ='foreground' if self.fgHilite.get() else 'background'
-        sampleElement = self.themeElements[self.highlightTarget.get()][0]
-        self.textHighlightSample.tag_config(sampleElement, **{plane:newColour})
-        theme = self.customTheme.get()
-        themeElement = sampleElement + '-' + plane
-        self.AddChangedItem('highlight', theme, themeElement, newColour)
-
-    def GetNewThemeName(self, message):
-        usedNames = (idleConf.GetSectionList('user', 'highlight') +
-                idleConf.GetSectionList('default', 'highlight'))
-        newTheme = SectionName(
-                self, 'New Custom Theme', message, usedNames).result
-        return newTheme
-
-    def SaveAsNewTheme(self):
-        newThemeName = self.GetNewThemeName('New Theme Name:')
-        if newThemeName:
-            self.CreateNewTheme(newThemeName)
-
-    def CreateNewTheme(self, newThemeName):
-        #creates new custom theme based on the previously active theme,
-        #and makes the new theme active
-        if self.themeIsBuiltin.get():
-            themeType = 'default'
-            themeName = self.builtinTheme.get()
-        else:
-            themeType = 'user'
-            themeName = self.customTheme.get()
-        newTheme = idleConf.GetThemeDict(themeType, themeName)
-        #apply any of the old theme's unsaved changes to the new theme
-        if themeName in self.changedItems['highlight']:
-            themeChanges = self.changedItems['highlight'][themeName]
-            for element in themeChanges:
-                newTheme[element] = themeChanges[element]
-        #save the new theme
-        self.SaveNewTheme(newThemeName, newTheme)
-        #change gui over to the new theme
-        customThemeList = idleConf.GetSectionList('user', 'highlight')
-        customThemeList.sort()
-        self.optMenuThemeCustom.SetMenu(customThemeList, newThemeName)
-        self.themeIsBuiltin.set(0)
-        self.SetThemeType()
-
-    def OnListFontButtonRelease(self, event):
-        font = self.listFontName.get(ANCHOR)
-        self.fontName.set(font.lower())
-        self.SetFontSample()
-
-    def SetFontSample(self, event=None):
-        fontName = self.fontName.get()
-        fontWeight = tkFont.BOLD if self.fontBold.get() else tkFont.NORMAL
-        newFont = (fontName, self.fontSize.get(), fontWeight)
-        self.labelFontSample.config(font=newFont)
-        self.textHighlightSample.configure(font=newFont)
-
-    def SetHighlightTarget(self):
-        if self.highlightTarget.get() == 'Cursor':  #bg not possible
-            self.radioFg.config(state=DISABLED)
-            self.radioBg.config(state=DISABLED)
-            self.fgHilite.set(1)
-        else:  #both fg and bg can be set
-            self.radioFg.config(state=NORMAL)
-            self.radioBg.config(state=NORMAL)
-            self.fgHilite.set(1)
-        self.SetColourSample()
-
-    def SetColourSampleBinding(self, *args):
-        self.SetColourSample()
-
-    def SetColourSample(self):
-        #set the colour smaple area
-        tag = self.themeElements[self.highlightTarget.get()][0]
-        plane = 'foreground' if self.fgHilite.get() else 'background'
-        colour = self.textHighlightSample.tag_cget(tag, plane)
-        self.frameColourSet.config(bg=colour)
-
-    def PaintThemeSample(self):
-        if self.themeIsBuiltin.get():  #a default theme
-            theme = self.builtinTheme.get()
-        else:  #a user theme
-            theme = self.customTheme.get()
-        for elementTitle in self.themeElements:
-            element = self.themeElements[elementTitle][0]
-            colours = idleConf.GetHighlight(theme, element)
-            if element == 'cursor': #cursor sample needs special painting
-                colours['background'] = idleConf.GetHighlight(
-                        theme, 'normal', fgBg='bg')
-            #handle any unsaved changes to this theme
-            if theme in self.changedItems['highlight']:
-                themeDict = self.changedItems['highlight'][theme]
-                if element + '-foreground' in themeDict:
-                    colours['foreground'] = themeDict[element + '-foreground']
-                if element + '-background' in themeDict:
-                    colours['background'] = themeDict[element + '-background']
-            self.textHighlightSample.tag_config(element, **colours)
-        self.SetColourSample()
-
-    def HelpSourceSelected(self, event):
-        self.SetHelpListButtonStates()
-
-    def SetHelpListButtonStates(self):
-        if self.listHelp.size() < 1:  #no entries in list
-            self.buttonHelpListEdit.config(state=DISABLED)
-            self.buttonHelpListRemove.config(state=DISABLED)
-        else: #there are some entries
-            if self.listHelp.curselection():  #there currently is a selection
-                self.buttonHelpListEdit.config(state=NORMAL)
-                self.buttonHelpListRemove.config(state=NORMAL)
-            else:  #there currently is not a selection
-                self.buttonHelpListEdit.config(state=DISABLED)
-                self.buttonHelpListRemove.config(state=DISABLED)
-
-    def HelpListItemAdd(self):
-        helpSource = HelpSource(self, 'New Help Source',
-                                ).result
-        if helpSource:
-            self.userHelpList.append((helpSource[0], helpSource[1]))
-            self.listHelp.insert(END, helpSource[0])
-            self.UpdateUserHelpChangedItems()
-        self.SetHelpListButtonStates()
-
-    def HelpListItemEdit(self):
-        itemIndex = self.listHelp.index(ANCHOR)
-        helpSource = self.userHelpList[itemIndex]
-        newHelpSource = HelpSource(
-                self, 'Edit Help Source',
-                menuitem=helpSource[0],
-                filepath=helpSource[1],
-                ).result
-        if newHelpSource and newHelpSource != helpSource:
-            self.userHelpList[itemIndex] = newHelpSource
-            self.listHelp.delete(itemIndex)
-            self.listHelp.insert(itemIndex, newHelpSource[0])
-            self.UpdateUserHelpChangedItems()
-            self.SetHelpListButtonStates()
-
-    def HelpListItemRemove(self):
-        itemIndex = self.listHelp.index(ANCHOR)
-        del(self.userHelpList[itemIndex])
-        self.listHelp.delete(itemIndex)
-        self.UpdateUserHelpChangedItems()
-        self.SetHelpListButtonStates()
-
-    def UpdateUserHelpChangedItems(self):
-        "Clear and rebuild the HelpFiles section in self.changedItems"
-        self.changedItems['main']['HelpFiles'] = {}
-        for num in range(1, len(self.userHelpList) + 1):
-            self.AddChangedItem(
-                    'main', 'HelpFiles', str(num),
-                    ';'.join(self.userHelpList[num-1][:2]))
+    def help(self):
+        """Create textview for config dialog help.
 
-    def LoadFontCfg(self):
-        ##base editor font selection list
-        fonts = list(tkFont.families(self))
-        fonts.sort()
-        for font in fonts:
-            self.listFontName.insert(END, font)
-        configuredFont = idleConf.GetFont(self, 'main', 'EditorWindow')
-        fontName = configuredFont[0].lower()
-        fontSize = configuredFont[1]
-        fontBold  = configuredFont[2]=='bold'
-        self.fontName.set(fontName)
-        lc_fonts = [s.lower() for s in fonts]
-        try:
-            currentFontIndex = lc_fonts.index(fontName)
-            self.listFontName.see(currentFontIndex)
-            self.listFontName.select_set(currentFontIndex)
-            self.listFontName.select_anchor(currentFontIndex)
-        except ValueError:
-            pass
-        ##font size dropdown
-        self.optMenuFontSize.SetMenu(('7', '8', '9', '10', '11', '12', '13',
-                                      '14', '16', '18', '20', '22',
-                                      '25', '29', '34', '40'), fontSize )
-        ##fontWeight
-        self.fontBold.set(fontBold)
-        ##font sample
-        self.SetFontSample()
-
-    def LoadTabCfg(self):
-        ##indent sizes
-        spaceNum = idleConf.GetOption(
-            'main', 'Indent', 'num-spaces', default=4, type='int')
-        self.spaceNum.set(spaceNum)
+        Attrbutes accessed:
+            note
 
-    def LoadThemeCfg(self):
-        ##current theme type radiobutton
-        self.themeIsBuiltin.set(idleConf.GetOption(
-                'main', 'Theme', 'default', type='bool', default=1))
-        ##currently set theme
-        currentOption = idleConf.CurrentTheme()
-        ##load available theme option menus
-        if self.themeIsBuiltin.get(): #default theme selected
-            itemList = idleConf.GetSectionList('default', 'highlight')
-            itemList.sort()
-            self.optMenuThemeBuiltin.SetMenu(itemList, currentOption)
-            itemList = idleConf.GetSectionList('user', 'highlight')
-            itemList.sort()
-            if not itemList:
-                self.radioThemeCustom.config(state=DISABLED)
-                self.customTheme.set('- no custom themes -')
-            else:
-                self.optMenuThemeCustom.SetMenu(itemList, itemList[0])
-        else: #user theme selected
-            itemList = idleConf.GetSectionList('user', 'highlight')
-            itemList.sort()
-            self.optMenuThemeCustom.SetMenu(itemList, currentOption)
-            itemList = idleConf.GetSectionList('default', 'highlight')
-            itemList.sort()
-            self.optMenuThemeBuiltin.SetMenu(itemList, itemList[0])
-        self.SetThemeType()
-        ##load theme element option menu
-        themeNames = list(self.themeElements.keys())
-        themeNames.sort(key=lambda x: self.themeElements[x][1])
-        self.optMenuHighlightTarget.SetMenu(themeNames, themeNames[0])
-        self.PaintThemeSample()
-        self.SetHighlightTarget()
-
-    def LoadKeyCfg(self):
-        ##current keys type radiobutton
-        self.keysAreBuiltin.set(idleConf.GetOption(
-                'main', 'Keys', 'default', type='bool', default=1))
-        ##currently set keys
-        currentOption = idleConf.CurrentKeys()
-        ##load available keyset option menus
-        if self.keysAreBuiltin.get(): #default theme selected
-            itemList = idleConf.GetSectionList('default', 'keys')
-            itemList.sort()
-            self.optMenuKeysBuiltin.SetMenu(itemList, currentOption)
-            itemList = idleConf.GetSectionList('user', 'keys')
-            itemList.sort()
-            if not itemList:
-                self.radioKeysCustom.config(state=DISABLED)
-                self.customKeys.set('- no custom keys -')
-            else:
-                self.optMenuKeysCustom.SetMenu(itemList, itemList[0])
-        else: #user key set selected
-            itemList = idleConf.GetSectionList('user', 'keys')
-            itemList.sort()
-            self.optMenuKeysCustom.SetMenu(itemList, currentOption)
-            itemList = idleConf.GetSectionList('default', 'keys')
-            itemList.sort()
-            self.optMenuKeysBuiltin.SetMenu(itemList, idleConf.default_keys())
-        self.SetKeysType()
-        ##load keyset element list
-        keySetName = idleConf.CurrentKeys()
-        self.LoadKeysList(keySetName)
-
-    def LoadGeneralCfg(self):
-        #startup state
-        self.startupEdit.set(idleConf.GetOption(
-                'main', 'General', 'editor-on-startup', default=1, type='bool'))
-        #autosave state
-        self.autoSave.set(idleConf.GetOption(
-                'main', 'General', 'autosave', default=0, type='bool'))
-        #initial window size
-        self.winWidth.set(idleConf.GetOption(
-                'main', 'EditorWindow', 'width', type='int'))
-        self.winHeight.set(idleConf.GetOption(
-                'main', 'EditorWindow', 'height', type='int'))
-        # default source encoding
-        self.encoding.set(idleConf.GetOption(
-                'main', 'EditorWindow', 'encoding', default='none'))
-        # additional help sources
-        self.userHelpList = idleConf.GetAllExtraHelpSourcesList()
-        for helpItem in self.userHelpList:
-            self.listHelp.insert(END, helpItem[0])
-        self.SetHelpListButtonStates()
-
-    def LoadConfigs(self):
-        """
-        load configuration from default and user config files and populate
-        the widgets on the config dialog pages.
-        """
-        ### fonts / tabs page
-        self.LoadFontCfg()
-        self.LoadTabCfg()
-        ### highlighting page
-        self.LoadThemeCfg()
-        ### keys page
-        self.LoadKeyCfg()
-        ### general page
-        self.LoadGeneralCfg()
-        # note: extension page handled separately
-
-    def SaveNewKeySet(self, keySetName, keySet):
-        """
-        save a newly created core key set.
-        keySetName - string, the name of the new key set
-        keySet - dictionary containing the new key set
+        Methods:
+            view_text: Method from textview module.
         """
-        if not idleConf.userCfg['keys'].has_section(keySetName):
-            idleConf.userCfg['keys'].add_section(keySetName)
-        for event in keySet:
-            value = keySet[event]
-            idleConf.userCfg['keys'].SetOption(keySetName, event, value)
+        page = self.note.tab(self.note.select(), option='text').strip()
+        view_text(self, title='Help for IDLE preferences',
+                 text=help_common+help_pages.get(page, ''))
 
-    def SaveNewTheme(self, themeName, theme):
-        """
-        save a newly created theme.
-        themeName - string, the name of the new theme
-        theme - dictionary containing the new theme
+    def deactivate_current_config(self):
+        """Remove current key bindings.
+        Iterate over window instances defined in parent and remove
+        the keybindings.
         """
-        if not idleConf.userCfg['highlight'].has_section(themeName):
-            idleConf.userCfg['highlight'].add_section(themeName)
-        for element in theme:
-            value = theme[element]
-            idleConf.userCfg['highlight'].SetOption(themeName, element, value)
-
-    def SetUserValue(self, configType, section, item, value):
-        if idleConf.defaultCfg[configType].has_option(section, item):
-            if idleConf.defaultCfg[configType].Get(section, item) == value:
-                #the setting equals a default setting, remove it from user cfg
-                return idleConf.userCfg[configType].RemoveOption(section, item)
-        #if we got here set the option
-        return idleConf.userCfg[configType].SetOption(section, item, value)
-
-    def SaveAllChangedConfigs(self):
-        "Save configuration changes to the user config file."
-        idleConf.userCfg['main'].Save()
-        for configType in self.changedItems:
-            cfgTypeHasChanges = False
-            for section in self.changedItems[configType]:
-                if section == 'HelpFiles':
-                    #this section gets completely replaced
-                    idleConf.userCfg['main'].remove_section('HelpFiles')
-                    cfgTypeHasChanges = True
-                for item in self.changedItems[configType][section]:
-                    value = self.changedItems[configType][section][item]
-                    if self.SetUserValue(configType, section, item, value):
-                        cfgTypeHasChanges = True
-            if cfgTypeHasChanges:
-                idleConf.userCfg[configType].Save()
-        for configType in ['keys', 'highlight']:
-            # save these even if unchanged!
-            idleConf.userCfg[configType].Save()
-        self.ResetChangedItems() #clear the changed items dict
-        self.save_all_changed_extensions()  # uses a different mechanism
-
-    def DeactivateCurrentConfig(self):
-        #Before a config is saved, some cleanup of current
-        #config must be done - remove the previous keybindings
-        winInstances = self.parent.instance_dict.keys()
-        for instance in winInstances:
+        # Before a config is saved, some cleanup of current
+        # config must be done - remove the previous keybindings.
+        win_instances = self.parent.instance_dict.keys()
+        for instance in win_instances:
             instance.RemoveKeybindings()
 
-    def ActivateConfigChanges(self):
-        "Dynamically apply configuration changes"
-        winInstances = self.parent.instance_dict.keys()
-        for instance in winInstances:
+    def activate_config_changes(self):
+        """Apply configuration changes to current windows.
+
+        Dynamically update the current parent window instances
+        with some of the configuration changes.
+        """
+        win_instances = self.parent.instance_dict.keys()
+        for instance in win_instances:
             instance.ResetColorizer()
             instance.ResetFont()
             instance.set_notabs_indentwidth()
             instance.ApplyKeybindings()
             instance.reset_help_menu_entries()
+        for klass in reloadables:
+            klass.reload()
 
-    def Cancel(self):
-        self.destroy()
-
-    def Ok(self):
-        self.Apply()
-        self.destroy()
-
-    def Apply(self):
-        self.DeactivateCurrentConfig()
-        self.SaveAllChangedConfigs()
-        self.ActivateConfigChanges()
-
-    def Help(self):
-        page = self.tabPages._current_page
-        view_text(self, title='Help for IDLE preferences',
-                 text=help_common+help_pages.get(page, ''))
-
-    def CreatePageExtensions(self):
+    def create_page_extensions(self):
         """Part of the config dialog used for configuring IDLE extensions.
 
         This code is generic - it works for any and all IDLE extensions.
@@ -1235,15 +245,22 @@ class ConfigDialog(Toplevel):
         All values are treated as text, and it is up to the user to supply
         reasonable values. The only exception to this are the 'enable*' options,
         which are boolean, and can be toggled with a True/False button.
+
+        Methods:
+            load_extensions:
+            extension_selected: Handle selection from list.
+            create_extension_frame: Hold widgets for one extension.
+            set_extension_value: Set in userCfg['extensions'].
+            save_all_changed_extensions: Call extension page Save().
         """
         parent = self.parent
-        frame = self.tabPages.pages['Extensions'].frame
+        frame = Frame(self.note)
         self.ext_defaultCfg = idleConf.defaultCfg['extensions']
         self.ext_userCfg = idleConf.userCfg['extensions']
         self.is_int = self.register(is_int)
         self.load_extensions()
-        # create widgets - a listbox shows all available extensions, with the
-        # controls for the extension selected in the listbox to the right
+        # Create widgets - a listbox shows all available extensions, with the
+        # controls for the extension selected in the listbox to the right.
         self.extension_names = StringVar(self)
         frame.rowconfigure(0, weight=1)
         frame.columnconfigure(2, weight=1)
@@ -1256,14 +273,14 @@ class ConfigDialog(Toplevel):
         self.extension_list.grid(column=0, row=0, sticky='nws')
         scroll.grid(column=1, row=0, sticky='ns')
         self.details_frame.grid(column=2, row=0, sticky='nsew', padx=[10, 0])
-        frame.configure(padx=10, pady=10)
+        frame.configure(padding=10)
         self.config_frame = {}
         self.current_extension = None
 
         self.outerframe = self                      # TEMPORARY
         self.tabbed_page_set = self.extension_list  # TEMPORARY
 
-        # create the frame holding controls for each extension
+        # Create the frame holding controls for each extension.
         ext_names = ''
         for ext_name in sorted(self.extensions):
             self.create_extension_frame(ext_name)
@@ -1272,16 +289,19 @@ class ConfigDialog(Toplevel):
         self.extension_list.selection_set(0)
         self.extension_selected(None)
 
+        return frame
+
     def load_extensions(self):
         "Fill self.extensions with data from the default and user configs."
         self.extensions = {}
         for ext_name in idleConf.GetExtensions(active_only=False):
+            # Former built-in extensions are already filtered out.
             self.extensions[ext_name] = []
 
         for ext_name in self.extensions:
             opt_list = sorted(self.ext_defaultCfg.GetOptionList(ext_name))
 
-            # bring 'enable' options to the beginning of the list
+            # Bring 'enable' options to the beginning of the list.
             enables = [opt_name for opt_name in opt_list
                        if opt_name.startswith('enable')]
             for opt_name in enables:
@@ -1305,8 +325,8 @@ class ConfigDialog(Toplevel):
                     value = self.ext_userCfg.Get(
                             ext_name, opt_name, type=opt_type, raw=True,
                             default=def_obj)
-                except ValueError:  # Need this until .Get fixed
-                    value = def_obj  # bad values overwritten by entry
+                except ValueError:  # Need this until .Get fixed.
+                    value = def_obj  # Bad values overwritten by entry.
                 var = StringVar(self)
                 var.set(str(value))
 
@@ -1318,6 +338,7 @@ class ConfigDialog(Toplevel):
                                                  })
 
     def extension_selected(self, event):
+        "Handle selection of an extension from the list."
         newsel = self.extension_list.curselection()
         if newsel:
             newsel = self.extension_list.get(newsel)
@@ -1336,16 +357,15 @@ class ConfigDialog(Toplevel):
         f = VerticalScrolledFrame(self.details_frame, height=250, width=250)
         self.config_frame[ext_name] = f
         entry_area = f.interior
-        # create an entry for each configuration option
+        # Create an entry for each configuration option.
         for row, opt in enumerate(self.extensions[ext_name]):
-            # create a row with a label and entry/checkbutton
+            # Create a row with a label and entry/checkbutton.
             label = Label(entry_area, text=opt['name'])
             label.grid(row=row, column=0, sticky=NW)
             var = opt['var']
             if opt['type'] == 'bool':
-                Checkbutton(entry_area, textvariable=var, variable=var,
-                            onvalue='True', offvalue='False',
-                            indicatoron=FALSE, selectcolor='', width=8
+                Checkbutton(entry_area, variable=var,
+                            onvalue='True', offvalue='False', width=8
                             ).grid(row=row, column=1, sticky=W, padx=7)
             elif opt['type'] == 'int':
                 Entry(entry_area, textvariable=var, validate='key',
@@ -1358,19 +378,31 @@ class ConfigDialog(Toplevel):
         return
 
     def set_extension_value(self, section, opt):
+        """Return True if the configuration was added or changed.
+
+        If the value is the same as the default, then remove it
+        from user config file.
+        """
         name = opt['name']
         default = opt['default']
         value = opt['var'].get().strip() or default
         opt['var'].set(value)
         # if self.defaultCfg.has_section(section):
-        # Currently, always true; if not, indent to return
+        # Currently, always true; if not, indent to return.
         if (value == default):
             return self.ext_userCfg.RemoveOption(section, name)
-        # set the option
+        # Set the option.
         return self.ext_userCfg.SetOption(section, name, value)
 
     def save_all_changed_extensions(self):
-        """Save configuration changes to the user config file."""
+        """Save configuration changes to the user config file.
+
+        Attributes accessed:
+            extensions
+
+        Methods:
+            set_extension_value
+        """
         has_changes = False
         for ext_name in self.extensions:
             options = self.extensions[ext_name]
@@ -1381,6 +413,1696 @@ class ConfigDialog(Toplevel):
             self.ext_userCfg.Save()
 
 
+# class TabPage(Frame):  # A template for Page classes.
+#     def __init__(self, master):
+#         super().__init__(master)
+#         self.create_page_tab()
+#         self.load_tab_cfg()
+#     def create_page_tab(self):
+#         # Define tk vars and register var and callback with tracers.
+#         # Create subframes and widgets.
+#         # Pack widgets.
+#     def load_tab_cfg(self):
+#         # Initialize widgets with data from idleConf.
+#     def var_changed_var_name():
+#         # For each tk var that needs other than default callback.
+#     def other_methods():
+#         # Define tab-specific behavior.
+
+
+class FontPage(Frame):
+
+    def __init__(self, master, highpage):
+        super().__init__(master)
+        self.highlight_sample = highpage.highlight_sample
+        self.create_page_font_tab()
+        self.load_font_cfg()
+        self.load_tab_cfg()
+
+    def create_page_font_tab(self):
+        """Return frame of widgets for Font/Tabs tab.
+
+        Fonts: Enable users to provisionally change font face, size, or
+        boldness and to see the consequence of proposed choices.  Each
+        action set 3 options in changes structuree and changes the
+        corresponding aspect of the font sample on this page and
+        highlight sample on highlight page.
+
+        Function load_font_cfg initializes font vars and widgets from
+        idleConf entries and tk.
+
+        Fontlist: mouse button 1 click or up or down key invoke
+        on_fontlist_select(), which sets var font_name.
+
+        Sizelist: clicking the menubutton opens the dropdown menu. A
+        mouse button 1 click or return key sets var font_size.
+
+        Bold_toggle: clicking the box toggles var font_bold.
+
+        Changing any of the font vars invokes var_changed_font, which
+        adds all 3 font options to changes and calls set_samples.
+        Set_samples applies a new font constructed from the font vars to
+        font_sample and to highlight_sample on the hightlight page.
+
+        Tabs: Enable users to change spaces entered for indent tabs.
+        Changing indent_scale value with the mouse sets Var space_num,
+        which invokes the default callback to add an entry to
+        changes.  Load_tab_cfg initializes space_num to default.
+
+        Widgets for FontPage(Frame):  (*) widgets bound to self
+            frame_font: LabelFrame
+                frame_font_name: Frame
+                    font_name_title: Label
+                    (*)fontlist: ListBox - font_name
+                    scroll_font: Scrollbar
+                frame_font_param: Frame
+                    font_size_title: Label
+                    (*)sizelist: DynOptionMenu - font_size
+                    (*)bold_toggle: Checkbutton - font_bold
+                frame_font_sample: Frame
+                    (*)font_sample: Label
+            frame_indent: LabelFrame
+                    indent_title: Label
+                    (*)indent_scale: Scale - space_num
+        """
+        self.font_name = tracers.add(StringVar(self), self.var_changed_font)
+        self.font_size = tracers.add(StringVar(self), self.var_changed_font)
+        self.font_bold = tracers.add(BooleanVar(self), self.var_changed_font)
+        self.space_num = tracers.add(IntVar(self), ('main', 'Indent', 'num-spaces'))
+
+        # Create widgets:
+        # body and body section frames.
+        frame_font = LabelFrame(
+                self, borderwidth=2, relief=GROOVE, text=' Base Editor Font ')
+        frame_indent = LabelFrame(
+                self, borderwidth=2, relief=GROOVE, text=' Indentation Width ')
+        # frame_font.
+        frame_font_name = Frame(frame_font)
+        frame_font_param = Frame(frame_font)
+        font_name_title = Label(
+                frame_font_name, justify=LEFT, text='Font Face :')
+        self.fontlist = Listbox(frame_font_name, height=5,
+                                takefocus=True, exportselection=FALSE)
+        self.fontlist.bind('<ButtonRelease-1>', self.on_fontlist_select)
+        self.fontlist.bind('<KeyRelease-Up>', self.on_fontlist_select)
+        self.fontlist.bind('<KeyRelease-Down>', self.on_fontlist_select)
+        scroll_font = Scrollbar(frame_font_name)
+        scroll_font.config(command=self.fontlist.yview)
+        self.fontlist.config(yscrollcommand=scroll_font.set)
+        font_size_title = Label(frame_font_param, text='Size :')
+        self.sizelist = DynOptionMenu(frame_font_param, self.font_size, None)
+        self.bold_toggle = Checkbutton(
+                frame_font_param, variable=self.font_bold,
+                onvalue=1, offvalue=0, text='Bold')
+        frame_font_sample = Frame(frame_font, relief=SOLID, borderwidth=1)
+        temp_font = tkFont.Font(self, ('courier', 10, 'normal'))
+        self.font_sample = Label(
+                frame_font_sample, justify=LEFT, font=temp_font,
+                text='AaBbCcDdEe\nFfGgHhIiJj\n1234567890\n#:+=(){}[]')
+        # frame_indent.
+        indent_title = Label(
+                frame_indent, justify=LEFT,
+                text='Python Standard: 4 Spaces!')
+        self.indent_scale = Scale(
+                frame_indent, variable=self.space_num,
+                orient='horizontal', tickinterval=2, from_=2, to=16)
+
+        # Pack widgets:
+        # body.
+        frame_font.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
+        frame_indent.pack(side=LEFT, padx=5, pady=5, fill=Y)
+        # frame_font.
+        frame_font_name.pack(side=TOP, padx=5, pady=5, fill=X)
+        frame_font_param.pack(side=TOP, padx=5, pady=5, fill=X)
+        font_name_title.pack(side=TOP, anchor=W)
+        self.fontlist.pack(side=LEFT, expand=TRUE, fill=X)
+        scroll_font.pack(side=LEFT, fill=Y)
+        font_size_title.pack(side=LEFT, anchor=W)
+        self.sizelist.pack(side=LEFT, anchor=W)
+        self.bold_toggle.pack(side=LEFT, anchor=W, padx=20)
+        frame_font_sample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
+        self.font_sample.pack(expand=TRUE, fill=BOTH)
+        # frame_indent.
+        frame_indent.pack(side=TOP, fill=X)
+        indent_title.pack(side=TOP, anchor=W, padx=5)
+        self.indent_scale.pack(side=TOP, padx=5, fill=X)
+
+    def load_font_cfg(self):
+        """Load current configuration settings for the font options.
+
+        Retrieve current font with idleConf.GetFont and font families
+        from tk. Setup fontlist and set font_name.  Setup sizelist,
+        which sets font_size.  Set font_bold.  Call set_samples.
+        """
+        configured_font = idleConf.GetFont(self, 'main', 'EditorWindow')
+        font_name = configured_font[0].lower()
+        font_size = configured_font[1]
+        font_bold  = configured_font[2]=='bold'
+
+        # Set editor font selection list and font_name.
+        fonts = list(tkFont.families(self))
+        fonts.sort()
+        for font in fonts:
+            self.fontlist.insert(END, font)
+        self.font_name.set(font_name)
+        lc_fonts = [s.lower() for s in fonts]
+        try:
+            current_font_index = lc_fonts.index(font_name)
+            self.fontlist.see(current_font_index)
+            self.fontlist.select_set(current_font_index)
+            self.fontlist.select_anchor(current_font_index)
+            self.fontlist.activate(current_font_index)
+        except ValueError:
+            pass
+        # Set font size dropdown.
+        self.sizelist.SetMenu(('7', '8', '9', '10', '11', '12', '13', '14',
+                               '16', '18', '20', '22', '25', '29', '34', '40'),
+                              font_size)
+        # Set font weight.
+        self.font_bold.set(font_bold)
+        self.set_samples()
+
+    def var_changed_font(self, *params):
+        """Store changes to font attributes.
+
+        When one font attribute changes, save them all, as they are
+        not independent from each other. In particular, when we are
+        overriding the default font, we need to write out everything.
+        """
+        value = self.font_name.get()
+        changes.add_option('main', 'EditorWindow', 'font', value)
+        value = self.font_size.get()
+        changes.add_option('main', 'EditorWindow', 'font-size', value)
+        value = self.font_bold.get()
+        changes.add_option('main', 'EditorWindow', 'font-bold', value)
+        self.set_samples()
+
+    def on_fontlist_select(self, event):
+        """Handle selecting a font from the list.
+
+        Event can result from either mouse click or Up or Down key.
+        Set font_name and example displays to selection.
+        """
+        font = self.fontlist.get(
+                ACTIVE if event.type.name == 'KeyRelease' else ANCHOR)
+        self.font_name.set(font.lower())
+
+    def set_samples(self, event=None):
+        """Update update both screen samples with the font settings.
+
+        Called on font initialization and change events.
+        Accesses font_name, font_size, and font_bold Variables.
+        Updates font_sample and hightlight page highlight_sample.
+        """
+        font_name = self.font_name.get()
+        font_weight = tkFont.BOLD if self.font_bold.get() else tkFont.NORMAL
+        new_font = (font_name, self.font_size.get(), font_weight)
+        self.font_sample['font'] = new_font
+        self.highlight_sample['font'] = new_font
+
+    def load_tab_cfg(self):
+        """Load current configuration settings for the tab options.
+
+        Attributes updated:
+            space_num: Set to value from idleConf.
+        """
+        # Set indent sizes.
+        space_num = idleConf.GetOption(
+            'main', 'Indent', 'num-spaces', default=4, type='int')
+        self.space_num.set(space_num)
+
+    def var_changed_space_num(self, *params):
+        "Store change to indentation size."
+        value = self.space_num.get()
+        changes.add_option('main', 'Indent', 'num-spaces', value)
+
+
+class HighPage(Frame):
+
+    def __init__(self, master):
+        super().__init__(master)
+        self.cd = master.master
+        self.style = Style(master)
+        self.create_page_highlight()
+        self.load_theme_cfg()
+
+    def create_page_highlight(self):
+        """Return frame of widgets for Highlighting tab.
+
+        Enable users to provisionally change foreground and background
+        colors applied to textual tags.  Color mappings are stored in
+        complete listings called themes.  Built-in themes in
+        idlelib/config-highlight.def are fixed as far as the dialog is
+        concerned. Any theme can be used as the base for a new custom
+        theme, stored in .idlerc/config-highlight.cfg.
+
+        Function load_theme_cfg() initializes tk variables and theme
+        lists and calls paint_theme_sample() and set_highlight_target()
+        for the current theme.  Radiobuttons builtin_theme_on and
+        custom_theme_on toggle var theme_source, which controls if the
+        current set of colors are from a builtin or custom theme.
+        DynOptionMenus builtinlist and customlist contain lists of the
+        builtin and custom themes, respectively, and the current item
+        from each list is stored in vars builtin_name and custom_name.
+
+        Function paint_theme_sample() applies the colors from the theme
+        to the tags in text widget highlight_sample and then invokes
+        set_color_sample().  Function set_highlight_target() sets the state
+        of the radiobuttons fg_on and bg_on based on the tag and it also
+        invokes set_color_sample().
+
+        Function set_color_sample() sets the background color for the frame
+        holding the color selector.  This provides a larger visual of the
+        color for the current tag and plane (foreground/background).
+
+        Note: set_color_sample() is called from many places and is often
+        called more than once when a change is made.  It is invoked when
+        foreground or background is selected (radiobuttons), from
+        paint_theme_sample() (theme is changed or load_cfg is called), and
+        from set_highlight_target() (target tag is changed or load_cfg called).
+
+        Button delete_custom invokes delete_custom() to delete
+        a custom theme from idleConf.userCfg['highlight'] and changes.
+        Button save_custom invokes save_as_new_theme() which calls
+        get_new_theme_name() and create_new() to save a custom theme
+        and its colors to idleConf.userCfg['highlight'].
+
+        Radiobuttons fg_on and bg_on toggle var fg_bg_toggle to control
+        if the current selected color for a tag is for the foreground or
+        background.
+
+        DynOptionMenu targetlist contains a readable description of the
+        tags applied to Python source within IDLE.  Selecting one of the
+        tags from this list populates highlight_target, which has a callback
+        function set_highlight_target().
+
+        Text widget highlight_sample displays a block of text (which is
+        mock Python code) in which is embedded the defined tags and reflects
+        the color attributes of the current theme and changes for those tags.
+        Mouse button 1 allows for selection of a tag and updates
+        highlight_target with that tag value.
+
+        Note: The font in highlight_sample is set through the config in
+        the fonts tab.
+
+        In other words, a tag can be selected either from targetlist or
+        by clicking on the sample text within highlight_sample.  The
+        plane (foreground/background) is selected via the radiobutton.
+        Together, these two (tag and plane) control what color is
+        shown in set_color_sample() for the current theme.  Button set_color
+        invokes get_color() which displays a ColorChooser to change the
+        color for the selected tag/plane.  If a new color is picked,
+        it will be saved to changes and the highlight_sample and
+        frame background will be updated.
+
+        Tk Variables:
+            color: Color of selected target.
+            builtin_name: Menu variable for built-in theme.
+            custom_name: Menu variable for custom theme.
+            fg_bg_toggle: Toggle for foreground/background color.
+                Note: this has no callback.
+            theme_source: Selector for built-in or custom theme.
+            highlight_target: Menu variable for the highlight tag target.
+
+        Instance Data Attributes:
+            theme_elements: Dictionary of tags for text highlighting.
+                The key is the display name and the value is a tuple of
+                (tag name, display sort order).
+
+        Methods [attachment]:
+            load_theme_cfg: Load current highlight colors.
+            get_color: Invoke colorchooser [button_set_color].
+            set_color_sample_binding: Call set_color_sample [fg_bg_toggle].
+            set_highlight_target: set fg_bg_toggle, set_color_sample().
+            set_color_sample: Set frame background to target.
+            on_new_color_set: Set new color and add option.
+            paint_theme_sample: Recolor sample.
+            get_new_theme_name: Get from popup.
+            create_new: Combine theme with changes and save.
+            save_as_new_theme: Save [button_save_custom].
+            set_theme_type: Command for [theme_source].
+            delete_custom: Activate default [button_delete_custom].
+            save_new: Save to userCfg['theme'] (is function).
+
+        Widgets of highlights page frame:  (*) widgets bound to self
+            frame_custom: LabelFrame
+                (*)highlight_sample: Text
+                (*)frame_color_set: Frame
+                    (*)button_set_color: Button
+                    (*)targetlist: DynOptionMenu - highlight_target
+                frame_fg_bg_toggle: Frame
+                    (*)fg_on: Radiobutton - fg_bg_toggle
+                    (*)bg_on: Radiobutton - fg_bg_toggle
+                (*)button_save_custom: Button
+            frame_theme: LabelFrame
+                theme_type_title: Label
+                (*)builtin_theme_on: Radiobutton - theme_source
+                (*)custom_theme_on: Radiobutton - theme_source
+                (*)builtinlist: DynOptionMenu - builtin_name
+                (*)customlist: DynOptionMenu - custom_name
+                (*)button_delete_custom: Button
+                (*)theme_message: Label
+        """
+        self.theme_elements = {
+            'Normal Text': ('normal', '00'),
+            'Python Keywords': ('keyword', '01'),
+            'Python Definitions': ('definition', '02'),
+            'Python Builtins': ('builtin', '03'),
+            'Python Comments': ('comment', '04'),
+            'Python Strings': ('string', '05'),
+            'Selected Text': ('hilite', '06'),
+            'Found Text': ('hit', '07'),
+            'Cursor': ('cursor', '08'),
+            'Editor Breakpoint': ('break', '09'),
+            'Shell Normal Text': ('console', '10'),
+            'Shell Error Text': ('error', '11'),
+            'Shell Stdout Text': ('stdout', '12'),
+            'Shell Stderr Text': ('stderr', '13'),
+            }
+        self.builtin_name = tracers.add(
+                StringVar(self), self.var_changed_builtin_name)
+        self.custom_name = tracers.add(
+                StringVar(self), self.var_changed_custom_name)
+        self.fg_bg_toggle = BooleanVar(self)
+        self.color = tracers.add(
+                StringVar(self), self.var_changed_color)
+        self.theme_source = tracers.add(
+                BooleanVar(self), self.var_changed_theme_source)
+        self.highlight_target = tracers.add(
+                StringVar(self), self.var_changed_highlight_target)
+
+        # Create widgets:
+        # body frame and section frames.
+        frame_custom = LabelFrame(self, borderwidth=2, relief=GROOVE,
+                                  text=' Custom Highlighting ')
+        frame_theme = LabelFrame(self, borderwidth=2, relief=GROOVE,
+                                 text=' Highlighting Theme ')
+        # frame_custom.
+        text = self.highlight_sample = Text(
+                frame_custom, relief=SOLID, borderwidth=1,
+                font=('courier', 12, ''), cursor='hand2', width=21, height=13,
+                takefocus=FALSE, highlightthickness=0, wrap=NONE)
+        text.bind('<Double-Button-1>', lambda e: 'break')
+        text.bind('<B1-Motion>', lambda e: 'break')
+        text_and_tags=(
+            ('\n', 'normal'),
+            ('#you can click here', 'comment'), ('\n', 'normal'),
+            ('#to choose items', 'comment'), ('\n', 'normal'),
+            ('def', 'keyword'), (' ', 'normal'),
+            ('func', 'definition'), ('(param):\n  ', 'normal'),
+            ('"""string"""', 'string'), ('\n  var0 = ', 'normal'),
+            ("'string'", 'string'), ('\n  var1 = ', 'normal'),
+            ("'selected'", 'hilite'), ('\n  var2 = ', 'normal'),
+            ("'found'", 'hit'), ('\n  var3 = ', 'normal'),
+            ('list', 'builtin'), ('(', 'normal'),
+            ('None', 'keyword'), (')\n', 'normal'),
+            ('  breakpoint("line")', 'break'), ('\n\n', 'normal'),
+            (' error ', 'error'), (' ', 'normal'),
+            ('cursor |', 'cursor'), ('\n ', 'normal'),
+            ('shell', 'console'), (' ', 'normal'),
+            ('stdout', 'stdout'), (' ', 'normal'),
+            ('stderr', 'stderr'), ('\n\n', 'normal'))
+        for texttag in text_and_tags:
+            text.insert(END, texttag[0], texttag[1])
+        for element in self.theme_elements:
+            def tem(event, elem=element):
+                # event.widget.winfo_top_level().highlight_target.set(elem)
+                self.highlight_target.set(elem)
+            text.tag_bind(
+                    self.theme_elements[element][0], '<ButtonPress-1>', tem)
+        text['state'] = 'disabled'
+        self.style.configure('frame_color_set.TFrame', borderwidth=1,
+                             relief='solid')
+        self.frame_color_set = Frame(frame_custom, style='frame_color_set.TFrame')
+        frame_fg_bg_toggle = Frame(frame_custom)
+        self.button_set_color = Button(
+                self.frame_color_set, text='Choose Color for :',
+                command=self.get_color)
+        self.targetlist = DynOptionMenu(
+                self.frame_color_set, self.highlight_target, None,
+                highlightthickness=0) #, command=self.set_highlight_targetBinding
+        self.fg_on = Radiobutton(
+                frame_fg_bg_toggle, variable=self.fg_bg_toggle, value=1,
+                text='Foreground', command=self.set_color_sample_binding)
+        self.bg_on = Radiobutton(
+                frame_fg_bg_toggle, variable=self.fg_bg_toggle, value=0,
+                text='Background', command=self.set_color_sample_binding)
+        self.fg_bg_toggle.set(1)
+        self.button_save_custom = Button(
+                frame_custom, text='Save as New Custom Theme',
+                command=self.save_as_new_theme)
+        # frame_theme.
+        theme_type_title = Label(frame_theme, text='Select : ')
+        self.builtin_theme_on = Radiobutton(
+                frame_theme, variable=self.theme_source, value=1,
+                command=self.set_theme_type, text='a Built-in Theme')
+        self.custom_theme_on = Radiobutton(
+                frame_theme, variable=self.theme_source, value=0,
+                command=self.set_theme_type, text='a Custom Theme')
+        self.builtinlist = DynOptionMenu(
+                frame_theme, self.builtin_name, None, command=None)
+        self.customlist = DynOptionMenu(
+                frame_theme, self.custom_name, None, command=None)
+        self.button_delete_custom = Button(
+                frame_theme, text='Delete Custom Theme',
+                command=self.delete_custom)
+        self.theme_message = Label(frame_theme, borderwidth=2)
+        # Pack widgets:
+        # body.
+        frame_custom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
+        frame_theme.pack(side=TOP, padx=5, pady=5, fill=X)
+        # frame_custom.
+        self.frame_color_set.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X)
+        frame_fg_bg_toggle.pack(side=TOP, padx=5, pady=0)
+        self.highlight_sample.pack(
+                side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
+        self.button_set_color.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4)
+        self.targetlist.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=3)
+        self.fg_on.pack(side=LEFT, anchor=E)
+        self.bg_on.pack(side=RIGHT, anchor=W)
+        self.button_save_custom.pack(side=BOTTOM, fill=X, padx=5, pady=5)
+        # frame_theme.
+        theme_type_title.pack(side=TOP, anchor=W, padx=5, pady=5)
+        self.builtin_theme_on.pack(side=TOP, anchor=W, padx=5)
+        self.custom_theme_on.pack(side=TOP, anchor=W, padx=5, pady=2)
+        self.builtinlist.pack(side=TOP, fill=X, padx=5, pady=5)
+        self.customlist.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5)
+        self.button_delete_custom.pack(side=TOP, fill=X, padx=5, pady=5)
+        self.theme_message.pack(side=TOP, fill=X, pady=5)
+
+    def load_theme_cfg(self):
+        """Load current configuration settings for the theme options.
+
+        Based on the theme_source toggle, the theme is set as
+        either builtin or custom and the initial widget values
+        reflect the current settings from idleConf.
+
+        Attributes updated:
+            theme_source: Set from idleConf.
+            builtinlist: List of default themes from idleConf.
+            customlist: List of custom themes from idleConf.
+            custom_theme_on: Disabled if there are no custom themes.
+            custom_theme: Message with additional information.
+            targetlist: Create menu from self.theme_elements.
+
+        Methods:
+            set_theme_type
+            paint_theme_sample
+            set_highlight_target
+        """
+        # Set current theme type radiobutton.
+        self.theme_source.set(idleConf.GetOption(
+                'main', 'Theme', 'default', type='bool', default=1))
+        # Set current theme.
+        current_option = idleConf.CurrentTheme()
+        # Load available theme option menus.
+        if self.theme_source.get():  # Default theme selected.
+            item_list = idleConf.GetSectionList('default', 'highlight')
+            item_list.sort()
+            self.builtinlist.SetMenu(item_list, current_option)
+            item_list = idleConf.GetSectionList('user', 'highlight')
+            item_list.sort()
+            if not item_list:
+                self.custom_theme_on.state(('disabled',))
+                self.custom_name.set('- no custom themes -')
+            else:
+                self.customlist.SetMenu(item_list, item_list[0])
+        else:  # User theme selected.
+            item_list = idleConf.GetSectionList('user', 'highlight')
+            item_list.sort()
+            self.customlist.SetMenu(item_list, current_option)
+            item_list = idleConf.GetSectionList('default', 'highlight')
+            item_list.sort()
+            self.builtinlist.SetMenu(item_list, item_list[0])
+        self.set_theme_type()
+        # Load theme element option menu.
+        theme_names = list(self.theme_elements.keys())
+        theme_names.sort(key=lambda x: self.theme_elements[x][1])
+        self.targetlist.SetMenu(theme_names, theme_names[0])
+        self.paint_theme_sample()
+        self.set_highlight_target()
+
+    def var_changed_builtin_name(self, *params):
+        """Process new builtin theme selection.
+
+        Add the changed theme's name to the changed_items and recreate
+        the sample with the values from the selected theme.
+        """
+        old_themes = ('IDLE Classic', 'IDLE New')
+        value = self.builtin_name.get()
+        if value not in old_themes:
+            if idleConf.GetOption('main', 'Theme', 'name') not in old_themes:
+                changes.add_option('main', 'Theme', 'name', old_themes[0])
+            changes.add_option('main', 'Theme', 'name2', value)
+            self.theme_message['text'] = 'New theme, see Help'
+        else:
+            changes.add_option('main', 'Theme', 'name', value)
+            changes.add_option('main', 'Theme', 'name2', '')
+            self.theme_message['text'] = ''
+        self.paint_theme_sample()
+
+    def var_changed_custom_name(self, *params):
+        """Process new custom theme selection.
+
+        If a new custom theme is selected, add the name to the
+        changed_items and apply the theme to the sample.
+        """
+        value = self.custom_name.get()
+        if value != '- no custom themes -':
+            changes.add_option('main', 'Theme', 'name', value)
+            self.paint_theme_sample()
+
+    def var_changed_theme_source(self, *params):
+        """Process toggle between builtin and custom theme.
+
+        Update the default toggle value and apply the newly
+        selected theme type.
+        """
+        value = self.theme_source.get()
+        changes.add_option('main', 'Theme', 'default', value)
+        if value:
+            self.var_changed_builtin_name()
+        else:
+            self.var_changed_custom_name()
+
+    def var_changed_color(self, *params):
+        "Process change to color choice."
+        self.on_new_color_set()
+
+    def var_changed_highlight_target(self, *params):
+        "Process selection of new target tag for highlighting."
+        self.set_highlight_target()
+
+    def set_theme_type(self):
+        """Set available screen options based on builtin or custom theme.
+
+        Attributes accessed:
+            theme_source
+
+        Attributes updated:
+            builtinlist
+            customlist
+            button_delete_custom
+            custom_theme_on
+
+        Called from:
+            handler for builtin_theme_on and custom_theme_on
+            delete_custom
+            create_new
+            load_theme_cfg
+        """
+        if self.theme_source.get():
+            self.builtinlist['state'] = 'normal'
+            self.customlist['state'] = 'disabled'
+            self.button_delete_custom.state(('disabled',))
+        else:
+            self.builtinlist['state'] = 'disabled'
+            self.custom_theme_on.state(('!disabled',))
+            self.customlist['state'] = 'normal'
+            self.button_delete_custom.state(('!disabled',))
+
+    def get_color(self):
+        """Handle button to select a new color for the target tag.
+
+        If a new color is selected while using a builtin theme, a
+        name must be supplied to create a custom theme.
+
+        Attributes accessed:
+            highlight_target
+            frame_color_set
+            theme_source
+
+        Attributes updated:
+            color
+
+        Methods:
+            get_new_theme_name
+            create_new
+        """
+        target = self.highlight_target.get()
+        prev_color = self.style.lookup(self.frame_color_set['style'],
+                                       'background')
+        rgbTuplet, color_string = tkColorChooser.askcolor(
+                parent=self, title='Pick new color for : '+target,
+                initialcolor=prev_color)
+        if color_string and (color_string != prev_color):
+            # User didn't cancel and they chose a new color.
+            if self.theme_source.get():  # Current theme is a built-in.
+                message = ('Your changes will be saved as a new Custom Theme. '
+                           'Enter a name for your new Custom Theme below.')
+                new_theme = self.get_new_theme_name(message)
+                if not new_theme:  # User cancelled custom theme creation.
+                    return
+                else:  # Create new custom theme based on previously active theme.
+                    self.create_new(new_theme)
+                    self.color.set(color_string)
+            else:  # Current theme is user defined.
+                self.color.set(color_string)
+
+    def on_new_color_set(self):
+        "Display sample of new color selection on the dialog."
+        new_color = self.color.get()
+        self.style.configure('frame_color_set.TFrame', background=new_color)
+        plane = 'foreground' if self.fg_bg_toggle.get() else 'background'
+        sample_element = self.theme_elements[self.highlight_target.get()][0]
+        self.highlight_sample.tag_config(sample_element, **{plane: new_color})
+        theme = self.custom_name.get()
+        theme_element = sample_element + '-' + plane
+        changes.add_option('highlight', theme, theme_element, new_color)
+
+    def get_new_theme_name(self, message):
+        "Return name of new theme from query popup."
+        used_names = (idleConf.GetSectionList('user', 'highlight') +
+                idleConf.GetSectionList('default', 'highlight'))
+        new_theme = SectionName(
+                self, 'New Custom Theme', message, used_names).result
+        return new_theme
+
+    def save_as_new_theme(self):
+        """Prompt for new theme name and create the theme.
+
+        Methods:
+            get_new_theme_name
+            create_new
+        """
+        new_theme_name = self.get_new_theme_name('New Theme Name:')
+        if new_theme_name:
+            self.create_new(new_theme_name)
+
+    def create_new(self, new_theme_name):
+        """Create a new custom theme with the given name.
+
+        Create the new theme based on the previously active theme
+        with the current changes applied.  Once it is saved, then
+        activate the new theme.
+
+        Attributes accessed:
+            builtin_name
+            custom_name
+
+        Attributes updated:
+            customlist
+            theme_source
+
+        Method:
+            save_new
+            set_theme_type
+        """
+        if self.theme_source.get():
+            theme_type = 'default'
+            theme_name = self.builtin_name.get()
+        else:
+            theme_type = 'user'
+            theme_name = self.custom_name.get()
+        new_theme = idleConf.GetThemeDict(theme_type, theme_name)
+        # Apply any of the old theme's unsaved changes to the new theme.
+        if theme_name in changes['highlight']:
+            theme_changes = changes['highlight'][theme_name]
+            for element in theme_changes:
+                new_theme[element] = theme_changes[element]
+        # Save the new theme.
+        self.save_new(new_theme_name, new_theme)
+        # Change GUI over to the new theme.
+        custom_theme_list = idleConf.GetSectionList('user', 'highlight')
+        custom_theme_list.sort()
+        self.customlist.SetMenu(custom_theme_list, new_theme_name)
+        self.theme_source.set(0)
+        self.set_theme_type()
+
+    def set_highlight_target(self):
+        """Set fg/bg toggle and color based on highlight tag target.
+
+        Instance variables accessed:
+            highlight_target
+
+        Attributes updated:
+            fg_on
+            bg_on
+            fg_bg_toggle
+
+        Methods:
+            set_color_sample
+
+        Called from:
+            var_changed_highlight_target
+            load_theme_cfg
+        """
+        if self.highlight_target.get() == 'Cursor':  # bg not possible
+            self.fg_on.state(('disabled',))
+            self.bg_on.state(('disabled',))
+            self.fg_bg_toggle.set(1)
+        else:  # Both fg and bg can be set.
+            self.fg_on.state(('!disabled',))
+            self.bg_on.state(('!disabled',))
+            self.fg_bg_toggle.set(1)
+        self.set_color_sample()
+
+    def set_color_sample_binding(self, *args):
+        """Change color sample based on foreground/background toggle.
+
+        Methods:
+            set_color_sample
+        """
+        self.set_color_sample()
+
+    def set_color_sample(self):
+        """Set the color of the frame background to reflect the selected target.
+
+        Instance variables accessed:
+            theme_elements
+            highlight_target
+            fg_bg_toggle
+            highlight_sample
+
+        Attributes updated:
+            frame_color_set
+        """
+        # Set the color sample area.
+        tag = self.theme_elements[self.highlight_target.get()][0]
+        plane = 'foreground' if self.fg_bg_toggle.get() else 'background'
+        color = self.highlight_sample.tag_cget(tag, plane)
+        self.style.configure('frame_color_set.TFrame', background=color)
+
+    def paint_theme_sample(self):
+        """Apply the theme colors to each element tag in the sample text.
+
+        Instance attributes accessed:
+            theme_elements
+            theme_source
+            builtin_name
+            custom_name
+
+        Attributes updated:
+            highlight_sample: Set the tag elements to the theme.
+
+        Methods:
+            set_color_sample
+
+        Called from:
+            var_changed_builtin_name
+            var_changed_custom_name
+            load_theme_cfg
+        """
+        if self.theme_source.get():  # Default theme
+            theme = self.builtin_name.get()
+        else:  # User theme
+            theme = self.custom_name.get()
+        for element_title in self.theme_elements:
+            element = self.theme_elements[element_title][0]
+            colors = idleConf.GetHighlight(theme, element)
+            if element == 'cursor':  # Cursor sample needs special painting.
+                colors['background'] = idleConf.GetHighlight(
+                        theme, 'normal', fgBg='bg')
+            # Handle any unsaved changes to this theme.
+            if theme in changes['highlight']:
+                theme_dict = changes['highlight'][theme]
+                if element + '-foreground' in theme_dict:
+                    colors['foreground'] = theme_dict[element + '-foreground']
+                if element + '-background' in theme_dict:
+                    colors['background'] = theme_dict[element + '-background']
+            self.highlight_sample.tag_config(element, **colors)
+        self.set_color_sample()
+
+    def save_new(self, theme_name, theme):
+        """Save a newly created theme to idleConf.
+
+        theme_name - string, the name of the new theme
+        theme - dictionary containing the new theme
+        """
+        if not idleConf.userCfg['highlight'].has_section(theme_name):
+            idleConf.userCfg['highlight'].add_section(theme_name)
+        for element in theme:
+            value = theme[element]
+            idleConf.userCfg['highlight'].SetOption(theme_name, element, value)
+
+    def askyesno(self, *args, **kwargs):
+        # Make testing easier.  Could change implementation.
+        return messagebox.askyesno(*args, **kwargs)
+
+    def delete_custom(self):
+        """Handle event to delete custom theme.
+
+        The current theme is deactivated and the default theme is
+        activated.  The custom theme is permanently removed from
+        the config file.
+
+        Attributes accessed:
+            custom_name
+
+        Attributes updated:
+            custom_theme_on
+            customlist
+            theme_source
+            builtin_name
+
+        Methods:
+            deactivate_current_config
+            save_all_changed_extensions
+            activate_config_changes
+            set_theme_type
+        """
+        theme_name = self.custom_name.get()
+        delmsg = 'Are you sure you wish to delete the theme %r ?'
+        if not self.askyesno(
+                'Delete Theme',  delmsg % theme_name, parent=self):
+            return
+        self.cd.deactivate_current_config()
+        # Remove theme from changes, config, and file.
+        changes.delete_section('highlight', theme_name)
+        # Reload user theme list.
+        item_list = idleConf.GetSectionList('user', 'highlight')
+        item_list.sort()
+        if not item_list:
+            self.custom_theme_on.state(('disabled',))
+            self.customlist.SetMenu(item_list, '- no custom themes -')
+        else:
+            self.customlist.SetMenu(item_list, item_list[0])
+        # Revert to default theme.
+        self.theme_source.set(idleConf.defaultCfg['main'].Get('Theme', 'default'))
+        self.builtin_name.set(idleConf.defaultCfg['main'].Get('Theme', 'name'))
+        # User can't back out of these changes, they must be applied now.
+        changes.save_all()
+        self.cd.save_all_changed_extensions()
+        self.cd.activate_config_changes()
+        self.set_theme_type()
+
+
+class KeysPage(Frame):
+
+    def __init__(self, master):
+        super().__init__(master)
+        self.cd = master.master
+        self.create_page_keys()
+        self.load_key_cfg()
+
+    def create_page_keys(self):
+        """Return frame of widgets for Keys tab.
+
+        Enable users to provisionally change both individual and sets of
+        keybindings (shortcut keys). Except for features implemented as
+        extensions, keybindings are stored in complete sets called
+        keysets. Built-in keysets in idlelib/config-keys.def are fixed
+        as far as the dialog is concerned. Any keyset can be used as the
+        base for a new custom keyset, stored in .idlerc/config-keys.cfg.
+
+        Function load_key_cfg() initializes tk variables and keyset
+        lists and calls load_keys_list for the current keyset.
+        Radiobuttons builtin_keyset_on and custom_keyset_on toggle var
+        keyset_source, which controls if the current set of keybindings
+        are from a builtin or custom keyset. DynOptionMenus builtinlist
+        and customlist contain lists of the builtin and custom keysets,
+        respectively, and the current item from each list is stored in
+        vars builtin_name and custom_name.
+
+        Button delete_custom_keys invokes delete_custom_keys() to delete
+        a custom keyset from idleConf.userCfg['keys'] and changes.  Button
+        save_custom_keys invokes save_as_new_key_set() which calls
+        get_new_keys_name() and create_new_key_set() to save a custom keyset
+        and its keybindings to idleConf.userCfg['keys'].
+
+        Listbox bindingslist contains all of the keybindings for the
+        selected keyset.  The keybindings are loaded in load_keys_list()
+        and are pairs of (event, [keys]) where keys can be a list
+        of one or more key combinations to bind to the same event.
+        Mouse button 1 click invokes on_bindingslist_select(), which
+        allows button_new_keys to be clicked.
+
+        So, an item is selected in listbindings, which activates
+        button_new_keys, and clicking button_new_keys calls function
+        get_new_keys().  Function get_new_keys() gets the key mappings from the
+        current keyset for the binding event item that was selected.  The
+        function then displays another dialog, GetKeysDialog, with the
+        selected binding event and current keys and allows new key sequences
+        to be entered for that binding event.  If the keys aren't
+        changed, nothing happens.  If the keys are changed and the keyset
+        is a builtin, function get_new_keys_name() will be called
+        for input of a custom keyset name.  If no name is given, then the
+        change to the keybinding will abort and no updates will be made.  If
+        a custom name is entered in the prompt or if the current keyset was
+        already custom (and thus didn't require a prompt), then
+        idleConf.userCfg['keys'] is updated in function create_new_key_set()
+        with the change to the event binding.  The item listing in bindingslist
+        is updated with the new keys.  Var keybinding is also set which invokes
+        the callback function, var_changed_keybinding, to add the change to
+        the 'keys' or 'extensions' changes tracker based on the binding type.
+
+        Tk Variables:
+            keybinding: Action/key bindings.
+
+        Methods:
+            load_keys_list: Reload active set.
+            create_new_key_set: Combine active keyset and changes.
+            set_keys_type: Command for keyset_source.
+            save_new_key_set: Save to idleConf.userCfg['keys'] (is function).
+            deactivate_current_config: Remove keys bindings in editors.
+
+        Widgets for KeysPage(frame):  (*) widgets bound to self
+            frame_key_sets: LabelFrame
+                frames[0]: Frame
+                    (*)builtin_keyset_on: Radiobutton - var keyset_source
+                    (*)custom_keyset_on: Radiobutton - var keyset_source
+                    (*)builtinlist: DynOptionMenu - var builtin_name,
+                            func keybinding_selected
+                    (*)customlist: DynOptionMenu - var custom_name,
+                            func keybinding_selected
+                    (*)keys_message: Label
+                frames[1]: Frame
+                    (*)button_delete_custom_keys: Button - delete_custom_keys
+                    (*)button_save_custom_keys: Button -  save_as_new_key_set
+            frame_custom: LabelFrame
+                frame_target: Frame
+                    target_title: Label
+                    scroll_target_y: Scrollbar
+                    scroll_target_x: Scrollbar
+                    (*)bindingslist: ListBox - on_bindingslist_select
+                    (*)button_new_keys: Button - get_new_keys & ..._name
+        """
+        self.builtin_name = tracers.add(
+                StringVar(self), self.var_changed_builtin_name)
+        self.custom_name = tracers.add(
+                StringVar(self), self.var_changed_custom_name)
+        self.keyset_source = tracers.add(
+                BooleanVar(self), self.var_changed_keyset_source)
+        self.keybinding = tracers.add(
+                StringVar(self), self.var_changed_keybinding)
+
+        # Create widgets:
+        # body and section frames.
+        frame_custom = LabelFrame(
+                self, borderwidth=2, relief=GROOVE,
+                text=' Custom Key Bindings ')
+        frame_key_sets = LabelFrame(
+                self, borderwidth=2, relief=GROOVE, text=' Key Set ')
+        # frame_custom.
+        frame_target = Frame(frame_custom)
+        target_title = Label(frame_target, text='Action - Key(s)')
+        scroll_target_y = Scrollbar(frame_target)
+        scroll_target_x = Scrollbar(frame_target, orient=HORIZONTAL)
+        self.bindingslist = Listbox(
+                frame_target, takefocus=FALSE, exportselection=FALSE)
+        self.bindingslist.bind('<ButtonRelease-1>',
+                               self.on_bindingslist_select)
+        scroll_target_y['command'] = self.bindingslist.yview
+        scroll_target_x['command'] = self.bindingslist.xview
+        self.bindingslist['yscrollcommand'] = scroll_target_y.set
+        self.bindingslist['xscrollcommand'] = scroll_target_x.set
+        self.button_new_keys = Button(
+                frame_custom, text='Get New Keys for Selection',
+                command=self.get_new_keys, state=DISABLED)
+        # frame_key_sets.
+        frames = [Frame(frame_key_sets, padding=2, borderwidth=0)
+                  for i in range(2)]
+        self.builtin_keyset_on = Radiobutton(
+                frames[0], variable=self.keyset_source, value=1,
+                command=self.set_keys_type, text='Use a Built-in Key Set')
+        self.custom_keyset_on = Radiobutton(
+                frames[0], variable=self.keyset_source, value=0,
+                command=self.set_keys_type, text='Use a Custom Key Set')
+        self.builtinlist = DynOptionMenu(
+                frames[0], self.builtin_name, None, command=None)
+        self.customlist = DynOptionMenu(
+                frames[0], self.custom_name, None, command=None)
+        self.button_delete_custom_keys = Button(
+                frames[1], text='Delete Custom Key Set',
+                command=self.delete_custom_keys)
+        self.button_save_custom_keys = Button(
+                frames[1], text='Save as New Custom Key Set',
+                command=self.save_as_new_key_set)
+        self.keys_message = Label(frames[0], borderwidth=2)
+
+        # Pack widgets:
+        # body.
+        frame_custom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH)
+        frame_key_sets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH)
+        # frame_custom.
+        self.button_new_keys.pack(side=BOTTOM, fill=X, padx=5, pady=5)
+        frame_target.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
+        # frame_target.
+        frame_target.columnconfigure(0, weight=1)
+        frame_target.rowconfigure(1, weight=1)
+        target_title.grid(row=0, column=0, columnspan=2, sticky=W)
+        self.bindingslist.grid(row=1, column=0, sticky=NSEW)
+        scroll_target_y.grid(row=1, column=1, sticky=NS)
+        scroll_target_x.grid(row=2, column=0, sticky=EW)
+        # frame_key_sets.
+        self.builtin_keyset_on.grid(row=0, column=0, sticky=W+NS)
+        self.custom_keyset_on.grid(row=1, column=0, sticky=W+NS)
+        self.builtinlist.grid(row=0, column=1, sticky=NSEW)
+        self.customlist.grid(row=1, column=1, sticky=NSEW)
+        self.keys_message.grid(row=0, column=2, sticky=NSEW, padx=5, pady=5)
+        self.button_delete_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2)
+        self.button_save_custom_keys.pack(side=LEFT, fill=X, expand=True, padx=2)
+        frames[0].pack(side=TOP, fill=BOTH, expand=True)
+        frames[1].pack(side=TOP, fill=X, expand=True, pady=2)
+
+    def load_key_cfg(self):
+        "Load current configuration settings for the keybinding options."
+        # Set current keys type radiobutton.
+        self.keyset_source.set(idleConf.GetOption(
+                'main', 'Keys', 'default', type='bool', default=1))
+        # Set current keys.
+        current_option = idleConf.CurrentKeys()
+        # Load available keyset option menus.
+        if self.keyset_source.get():  # Default theme selected.
+            item_list = idleConf.GetSectionList('default', 'keys')
+            item_list.sort()
+            self.builtinlist.SetMenu(item_list, current_option)
+            item_list = idleConf.GetSectionList('user', 'keys')
+            item_list.sort()
+            if not item_list:
+                self.custom_keyset_on.state(('disabled',))
+                self.custom_name.set('- no custom keys -')
+            else:
+                self.customlist.SetMenu(item_list, item_list[0])
+        else:  # User key set selected.
+            item_list = idleConf.GetSectionList('user', 'keys')
+            item_list.sort()
+            self.customlist.SetMenu(item_list, current_option)
+            item_list = idleConf.GetSectionList('default', 'keys')
+            item_list.sort()
+            self.builtinlist.SetMenu(item_list, idleConf.default_keys())
+        self.set_keys_type()
+        # Load keyset element list.
+        keyset_name = idleConf.CurrentKeys()
+        self.load_keys_list(keyset_name)
+
+    def var_changed_builtin_name(self, *params):
+        "Process selection of builtin key set."
+        old_keys = (
+            'IDLE Classic Windows',
+            'IDLE Classic Unix',
+            'IDLE Classic Mac',
+            'IDLE Classic OSX',
+        )
+        value = self.builtin_name.get()
+        if value not in old_keys:
+            if idleConf.GetOption('main', 'Keys', 'name') not in old_keys:
+                changes.add_option('main', 'Keys', 'name', old_keys[0])
+            changes.add_option('main', 'Keys', 'name2', value)
+            self.keys_message['text'] = 'New key set, see Help'
+        else:
+            changes.add_option('main', 'Keys', 'name', value)
+            changes.add_option('main', 'Keys', 'name2', '')
+            self.keys_message['text'] = ''
+        self.load_keys_list(value)
+
+    def var_changed_custom_name(self, *params):
+        "Process selection of custom key set."
+        value = self.custom_name.get()
+        if value != '- no custom keys -':
+            changes.add_option('main', 'Keys', 'name', value)
+            self.load_keys_list(value)
+
+    def var_changed_keyset_source(self, *params):
+        "Process toggle between builtin key set and custom key set."
+        value = self.keyset_source.get()
+        changes.add_option('main', 'Keys', 'default', value)
+        if value:
+            self.var_changed_builtin_name()
+        else:
+            self.var_changed_custom_name()
+
+    def var_changed_keybinding(self, *params):
+        "Store change to a keybinding."
+        value = self.keybinding.get()
+        key_set = self.custom_name.get()
+        event = self.bindingslist.get(ANCHOR).split()[0]
+        if idleConf.IsCoreBinding(event):
+            changes.add_option('keys', key_set, event, value)
+        else:  # Event is an extension binding.
+            ext_name = idleConf.GetExtnNameForEvent(event)
+            ext_keybind_section = ext_name + '_cfgBindings'
+            changes.add_option('extensions', ext_keybind_section, event, value)
+
+    def set_keys_type(self):
+        "Set available screen options based on builtin or custom key set."
+        if self.keyset_source.get():
+            self.builtinlist['state'] = 'normal'
+            self.customlist['state'] = 'disabled'
+            self.button_delete_custom_keys.state(('disabled',))
+        else:
+            self.builtinlist['state'] = 'disabled'
+            self.custom_keyset_on.state(('!disabled',))
+            self.customlist['state'] = 'normal'
+            self.button_delete_custom_keys.state(('!disabled',))
+
+    def get_new_keys(self):
+        """Handle event to change key binding for selected line.
+
+        A selection of a key/binding in the list of current
+        bindings pops up a dialog to enter a new binding.  If
+        the current key set is builtin and a binding has
+        changed, then a name for a custom key set needs to be
+        entered for the change to be applied.
+        """
+        list_index = self.bindingslist.index(ANCHOR)
+        binding = self.bindingslist.get(list_index)
+        bind_name = binding.split()[0]
+        if self.keyset_source.get():
+            current_key_set_name = self.builtin_name.get()
+        else:
+            current_key_set_name = self.custom_name.get()
+        current_bindings = idleConf.GetCurrentKeySet()
+        if current_key_set_name in changes['keys']:  # unsaved changes
+            key_set_changes = changes['keys'][current_key_set_name]
+            for event in key_set_changes:
+                current_bindings[event] = key_set_changes[event].split()
+        current_key_sequences = list(current_bindings.values())
+        new_keys = GetKeysDialog(self, 'Get New Keys', bind_name,
+                current_key_sequences).result
+        if new_keys:
+            if self.keyset_source.get():  # Current key set is a built-in.
+                message = ('Your changes will be saved as a new Custom Key Set.'
+                           ' Enter a name for your new Custom Key Set below.')
+                new_keyset = self.get_new_keys_name(message)
+                if not new_keyset:  # User cancelled custom key set creation.
+                    self.bindingslist.select_set(list_index)
+                    self.bindingslist.select_anchor(list_index)
+                    return
+                else:  # Create new custom key set based on previously active key set.
+                    self.create_new_key_set(new_keyset)
+            self.bindingslist.delete(list_index)
+            self.bindingslist.insert(list_index, bind_name+' - '+new_keys)
+            self.bindingslist.select_set(list_index)
+            self.bindingslist.select_anchor(list_index)
+            self.keybinding.set(new_keys)
+        else:
+            self.bindingslist.select_set(list_index)
+            self.bindingslist.select_anchor(list_index)
+
+    def get_new_keys_name(self, message):
+        "Return new key set name from query popup."
+        used_names = (idleConf.GetSectionList('user', 'keys') +
+                idleConf.GetSectionList('default', 'keys'))
+        new_keyset = SectionName(
+                self, 'New Custom Key Set', message, used_names).result
+        return new_keyset
+
+    def save_as_new_key_set(self):
+        "Prompt for name of new key set and save changes using that name."
+        new_keys_name = self.get_new_keys_name('New Key Set Name:')
+        if new_keys_name:
+            self.create_new_key_set(new_keys_name)
+
+    def on_bindingslist_select(self, event):
+        "Activate button to assign new keys to selected action."
+        self.button_new_keys.state(('!disabled',))
+
+    def create_new_key_set(self, new_key_set_name):
+        """Create a new custom key set with the given name.
+
+        Copy the bindings/keys from the previously active keyset
+        to the new keyset and activate the new custom keyset.
+        """
+        if self.keyset_source.get():
+            prev_key_set_name = self.builtin_name.get()
+        else:
+            prev_key_set_name = self.custom_name.get()
+        prev_keys = idleConf.GetCoreKeys(prev_key_set_name)
+        new_keys = {}
+        for event in prev_keys:  # Add key set to changed items.
+            event_name = event[2:-2]  # Trim off the angle brackets.
+            binding = ' '.join(prev_keys[event])
+            new_keys[event_name] = binding
+        # Handle any unsaved changes to prev key set.
+        if prev_key_set_name in changes['keys']:
+            key_set_changes = changes['keys'][prev_key_set_name]
+            for event in key_set_changes:
+                new_keys[event] = key_set_changes[event]
+        # Save the new key set.
+        self.save_new_key_set(new_key_set_name, new_keys)
+        # Change GUI over to the new key set.
+        custom_key_list = idleConf.GetSectionList('user', 'keys')
+        custom_key_list.sort()
+        self.customlist.SetMenu(custom_key_list, new_key_set_name)
+        self.keyset_source.set(0)
+        self.set_keys_type()
+
+    def load_keys_list(self, keyset_name):
+        """Reload the list of action/key binding pairs for the active key set.
+
+        An action/key binding can be selected to change the key binding.
+        """
+        reselect = False
+        if self.bindingslist.curselection():
+            reselect = True
+            list_index = self.bindingslist.index(ANCHOR)
+        keyset = idleConf.GetKeySet(keyset_name)
+        bind_names = list(keyset.keys())
+        bind_names.sort()
+        self.bindingslist.delete(0, END)
+        for bind_name in bind_names:
+            key = ' '.join(keyset[bind_name])
+            bind_name = bind_name[2:-2]  # Trim off the angle brackets.
+            if keyset_name in changes['keys']:
+                # Handle any unsaved changes to this key set.
+                if bind_name in changes['keys'][keyset_name]:
+                    key = changes['keys'][keyset_name][bind_name]
+            self.bindingslist.insert(END, bind_name+' - '+key)
+        if reselect:
+            self.bindingslist.see(list_index)
+            self.bindingslist.select_set(list_index)
+            self.bindingslist.select_anchor(list_index)
+
+    @staticmethod
+    def save_new_key_set(keyset_name, keyset):
+        """Save a newly created core key set.
+
+        Add keyset to idleConf.userCfg['keys'], not to disk.
+        If the keyset doesn't exist, it is created.  The
+        binding/keys are taken from the keyset argument.
+
+        keyset_name - string, the name of the new key set
+        keyset - dictionary containing the new keybindings
+        """
+        if not idleConf.userCfg['keys'].has_section(keyset_name):
+            idleConf.userCfg['keys'].add_section(keyset_name)
+        for event in keyset:
+            value = keyset[event]
+            idleConf.userCfg['keys'].SetOption(keyset_name, event, value)
+
+    def askyesno(self, *args, **kwargs):
+        # Make testing easier.  Could change implementation.
+        return messagebox.askyesno(*args, **kwargs)
+
+    def delete_custom_keys(self):
+        """Handle event to delete a custom key set.
+
+        Applying the delete deactivates the current configuration and
+        reverts to the default.  The custom key set is permanently
+        deleted from the config file.
+        """
+        keyset_name = self.custom_name.get()
+        delmsg = 'Are you sure you wish to delete the key set %r ?'
+        if not self.askyesno(
+                'Delete Key Set',  delmsg % keyset_name, parent=self):
+            return
+        self.cd.deactivate_current_config()
+        # Remove key set from changes, config, and file.
+        changes.delete_section('keys', keyset_name)
+        # Reload user key set list.
+        item_list = idleConf.GetSectionList('user', 'keys')
+        item_list.sort()
+        if not item_list:
+            self.custom_keyset_on.state(('disabled',))
+            self.customlist.SetMenu(item_list, '- no custom keys -')
+        else:
+            self.customlist.SetMenu(item_list, item_list[0])
+        # Revert to default key set.
+        self.keyset_source.set(idleConf.defaultCfg['main']
+                                .Get('Keys', 'default'))
+        self.builtin_name.set(idleConf.defaultCfg['main'].Get('Keys', 'name')
+                             or idleConf.default_keys())
+        # User can't back out of these changes, they must be applied now.
+        changes.save_all()
+        self.cd.save_all_changed_extensions()
+        self.cd.activate_config_changes()
+        self.set_keys_type()
+
+
+class GenPage(Frame):
+
+    def __init__(self, master):
+        super().__init__(master)
+        self.create_page_general()
+        self.load_general_cfg()
+
+    def create_page_general(self):
+        """Return frame of widgets for General tab.
+
+        Enable users to provisionally change general options. Function
+        load_general_cfg intializes tk variables and helplist using
+        idleConf.  Radiobuttons startup_shell_on and startup_editor_on
+        set var startup_edit. Radiobuttons save_ask_on and save_auto_on
+        set var autosave. Entry boxes win_width_int and win_height_int
+        set var win_width and win_height.  Setting var_name invokes the
+        default callback that adds option to changes.
+
+        Helplist: load_general_cfg loads list user_helplist with
+        name, position pairs and copies names to listbox helplist.
+        Clicking a name invokes help_source selected. Clicking
+        button_helplist_name invokes helplist_item_name, which also
+        changes user_helplist.  These functions all call
+        set_add_delete_state. All but load call update_help_changes to
+        rewrite changes['main']['HelpFiles'].
+
+        Widgets for GenPage(Frame):  (*) widgets bound to self
+            frame_window: LabelFrame
+                frame_run: Frame
+                    startup_title: Label
+                    (*)startup_editor_on: Radiobutton - startup_edit
+                    (*)startup_shell_on: Radiobutton - startup_edit
+                frame_win_size: Frame
+                    win_size_title: Label
+                    win_width_title: Label
+                    (*)win_width_int: Entry - win_width
+                    win_height_title: Label
+                    (*)win_height_int: Entry - win_height
+            frame_editor: LabelFrame
+                frame_save: Frame
+                    run_save_title: Label
+                    (*)save_ask_on: Radiobutton - autosave
+                    (*)save_auto_on: Radiobutton - autosave
+            frame_help: LabelFrame
+                frame_helplist: Frame
+                    frame_helplist_buttons: Frame
+                        (*)button_helplist_edit
+                        (*)button_helplist_add
+                        (*)button_helplist_remove
+                    (*)helplist: ListBox
+                    scroll_helplist: Scrollbar
+        """
+        # Integer values need StringVar because int('') raises.
+        self.startup_edit = tracers.add(
+                IntVar(self), ('main', 'General', 'editor-on-startup'))
+        self.win_width = tracers.add(
+                StringVar(self), ('main', 'EditorWindow', 'width'))
+        self.win_height = tracers.add(
+                StringVar(self), ('main', 'EditorWindow', 'height'))
+        self.autocomplete_wait = tracers.add(
+                StringVar(self), ('extensions', 'AutoComplete', 'popupwait'))
+        self.paren_style = tracers.add(
+                StringVar(self), ('extensions', 'ParenMatch', 'style'))
+        self.flash_delay = tracers.add(
+                StringVar(self), ('extensions', 'ParenMatch', 'flash-delay'))
+        self.paren_bell = tracers.add(
+                BooleanVar(self), ('extensions', 'ParenMatch', 'bell'))
+
+        self.autosave = tracers.add(
+                IntVar(self), ('main', 'General', 'autosave'))
+        self.format_width = tracers.add(
+                StringVar(self), ('extensions', 'FormatParagraph', 'max-width'))
+        self.context_lines = tracers.add(
+                StringVar(self), ('extensions', 'CodeContext', 'numlines'))
+
+        # Create widgets:
+        # Section frames.
+        frame_window = LabelFrame(self, borderwidth=2, relief=GROOVE,
+                                  text=' Window Preferences')
+        frame_editor = LabelFrame(self, borderwidth=2, relief=GROOVE,
+                                  text=' Editor Preferences')
+        frame_help = LabelFrame(self, borderwidth=2, relief=GROOVE,
+                               text=' Additional Help Sources ')
+        # Frame_window.
+        frame_run = Frame(frame_window, borderwidth=0)
+        startup_title = Label(frame_run, text='At Startup')
+        self.startup_editor_on = Radiobutton(
+                frame_run, variable=self.startup_edit, value=1,
+                text="Open Edit Window")
+        self.startup_shell_on = Radiobutton(
+                frame_run, variable=self.startup_edit, value=0,
+                text='Open Shell Window')
+
+        frame_win_size = Frame(frame_window, borderwidth=0)
+        win_size_title = Label(
+                frame_win_size, text='Initial Window Size  (in characters)')
+        win_width_title = Label(frame_win_size, text='Width')
+        self.win_width_int = Entry(
+                frame_win_size, textvariable=self.win_width, width=3)
+        win_height_title = Label(frame_win_size, text='Height')
+        self.win_height_int = Entry(
+                frame_win_size, textvariable=self.win_height, width=3)
+
+        frame_autocomplete = Frame(frame_window, borderwidth=0,)
+        auto_wait_title = Label(frame_autocomplete,
+                               text='Completions Popup Wait (milliseconds)')
+        self.auto_wait_int = Entry(frame_autocomplete, width=6,
+                                   textvariable=self.autocomplete_wait)
+
+        frame_paren1 = Frame(frame_window, borderwidth=0)
+        paren_style_title = Label(frame_paren1, text='Paren Match Style')
+        self.paren_style_type = OptionMenu(
+                frame_paren1, self.paren_style, 'expression',
+                "opener","parens","expression")
+        frame_paren2 = Frame(frame_window, borderwidth=0)
+        paren_time_title = Label(
+                frame_paren2, text='Time Match Displayed (milliseconds)\n'
+                                  '(0 is until next input)')
+        self.paren_flash_time = Entry(
+                frame_paren2, textvariable=self.flash_delay, width=6)
+        self.bell_on = Checkbutton(
+                frame_paren2, text="Bell on Mismatch", variable=self.paren_bell)
+
+        # Frame_editor.
+        frame_save = Frame(frame_editor, borderwidth=0)
+        run_save_title = Label(frame_save, text='At Start of Run (F5)  ')
+        self.save_ask_on = Radiobutton(
+                frame_save, variable=self.autosave, value=0,
+                text="Prompt to Save")
+        self.save_auto_on = Radiobutton(
+                frame_save, variable=self.autosave, value=1,
+                text='No Prompt')
+
+        frame_format = Frame(frame_editor, borderwidth=0)
+        format_width_title = Label(frame_format,
+                                   text='Format Paragraph Max Width')
+        self.format_width_int = Entry(
+                frame_format, textvariable=self.format_width, width=4)
+
+        frame_context = Frame(frame_editor, borderwidth=0)
+        context_title = Label(frame_context, text='Context Lines :')
+        self.context_int = Entry(
+                frame_context, textvariable=self.context_lines, width=3)
+
+
+        # frame_help.
+        frame_helplist = Frame(frame_help)
+        frame_helplist_buttons = Frame(frame_helplist)
+        self.helplist = Listbox(
+                frame_helplist, height=5, takefocus=True,
+                exportselection=FALSE)
+        scroll_helplist = Scrollbar(frame_helplist)
+        scroll_helplist['command'] = self.helplist.yview
+        self.helplist['yscrollcommand'] = scroll_helplist.set
+        self.helplist.bind('<ButtonRelease-1>', self.help_source_selected)
+        self.button_helplist_edit = Button(
+                frame_helplist_buttons, text='Edit', state='disabled',
+                width=8, command=self.helplist_item_edit)
+        self.button_helplist_add = Button(
+                frame_helplist_buttons, text='Add',
+                width=8, command=self.helplist_item_add)
+        self.button_helplist_remove = Button(
+                frame_helplist_buttons, text='Remove', state='disabled',
+                width=8, command=self.helplist_item_remove)
+
+        # Pack widgets:
+        # Body.
+        frame_window.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
+        frame_editor.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
+        frame_help.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
+        # frame_run.
+        frame_run.pack(side=TOP, padx=5, pady=0, fill=X)
+        startup_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
+        self.startup_shell_on.pack(side=RIGHT, anchor=W, padx=5, pady=5)
+        self.startup_editor_on.pack(side=RIGHT, anchor=W, padx=5, pady=5)
+        # frame_win_size.
+        frame_win_size.pack(side=TOP, padx=5, pady=0, fill=X)
+        win_size_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
+        self.win_height_int.pack(side=RIGHT, anchor=E, padx=10, pady=5)
+        win_height_title.pack(side=RIGHT, anchor=E, pady=5)
+        self.win_width_int.pack(side=RIGHT, anchor=E, padx=10, pady=5)
+        win_width_title.pack(side=RIGHT, anchor=E, pady=5)
+        # frame_autocomplete.
+        frame_autocomplete.pack(side=TOP, padx=5, pady=0, fill=X)
+        auto_wait_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
+        self.auto_wait_int.pack(side=TOP, padx=10, pady=5)
+        # frame_paren.
+        frame_paren1.pack(side=TOP, padx=5, pady=0, fill=X)
+        paren_style_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
+        self.paren_style_type.pack(side=TOP, padx=10, pady=5)
+        frame_paren2.pack(side=TOP, padx=5, pady=0, fill=X)
+        paren_time_title.pack(side=LEFT, anchor=W, padx=5)
+        self.bell_on.pack(side=RIGHT, anchor=E, padx=15, pady=5)
+        self.paren_flash_time.pack(side=TOP, anchor=W, padx=15, pady=5)
+
+        # frame_save.
+        frame_save.pack(side=TOP, padx=5, pady=0, fill=X)
+        run_save_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
+        self.save_auto_on.pack(side=RIGHT, anchor=W, padx=5, pady=5)
+        self.save_ask_on.pack(side=RIGHT, anchor=W, padx=5, pady=5)
+        # frame_format.
+        frame_format.pack(side=TOP, padx=5, pady=0, fill=X)
+        format_width_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
+        self.format_width_int.pack(side=TOP, padx=10, pady=5)
+        # frame_context.
+        frame_context.pack(side=TOP, padx=5, pady=0, fill=X)
+        context_title.pack(side=LEFT, anchor=W, padx=5, pady=5)
+        self.context_int.pack(side=TOP, padx=5, pady=5)
+
+        # frame_help.
+        frame_helplist_buttons.pack(side=RIGHT, padx=5, pady=5, fill=Y)
+        frame_helplist.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
+        scroll_helplist.pack(side=RIGHT, anchor=W, fill=Y)
+        self.helplist.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH)
+        self.button_helplist_edit.pack(side=TOP, anchor=W, pady=5)
+        self.button_helplist_add.pack(side=TOP, anchor=W)
+        self.button_helplist_remove.pack(side=TOP, anchor=W, pady=5)
+
+    def load_general_cfg(self):
+        "Load current configuration settings for the general options."
+        # Set variables for all windows.
+        self.startup_edit.set(idleConf.GetOption(
+                'main', 'General', 'editor-on-startup', type='bool'))
+        self.win_width.set(idleConf.GetOption(
+                'main', 'EditorWindow', 'width', type='int'))
+        self.win_height.set(idleConf.GetOption(
+                'main', 'EditorWindow', 'height', type='int'))
+        self.autocomplete_wait.set(idleConf.GetOption(
+                'extensions', 'AutoComplete', 'popupwait', type='int'))
+        self.paren_style.set(idleConf.GetOption(
+                'extensions', 'ParenMatch', 'style'))
+        self.flash_delay.set(idleConf.GetOption(
+                'extensions', 'ParenMatch', 'flash-delay', type='int'))
+        self.paren_bell.set(idleConf.GetOption(
+                'extensions', 'ParenMatch', 'bell'))
+
+        # Set variables for editor windows.
+        self.autosave.set(idleConf.GetOption(
+                'main', 'General', 'autosave', default=0, type='bool'))
+        self.format_width.set(idleConf.GetOption(
+                'extensions', 'FormatParagraph', 'max-width', type='int'))
+        self.context_lines.set(idleConf.GetOption(
+                'extensions', 'CodeContext', 'numlines', type='int'))
+
+        # Set additional help sources.
+        self.user_helplist = idleConf.GetAllExtraHelpSourcesList()
+        self.helplist.delete(0, 'end')
+        for help_item in self.user_helplist:
+            self.helplist.insert(END, help_item[0])
+        self.set_add_delete_state()
+
+    def help_source_selected(self, event):
+        "Handle event for selecting additional help."
+        self.set_add_delete_state()
+
+    def set_add_delete_state(self):
+        "Toggle the state for the help list buttons based on list entries."
+        if self.helplist.size() < 1:  # No entries in list.
+            self.button_helplist_edit.state(('disabled',))
+            self.button_helplist_remove.state(('disabled',))
+        else:  # Some entries.
+            if self.helplist.curselection():  # There currently is a selection.
+                self.button_helplist_edit.state(('!disabled',))
+                self.button_helplist_remove.state(('!disabled',))
+            else:  # There currently is not a selection.
+                self.button_helplist_edit.state(('disabled',))
+                self.button_helplist_remove.state(('disabled',))
+
+    def helplist_item_add(self):
+        """Handle add button for the help list.
+
+        Query for name and location of new help sources and add
+        them to the list.
+        """
+        help_source = HelpSource(self, 'New Help Source').result
+        if help_source:
+            self.user_helplist.append(help_source)
+            self.helplist.insert(END, help_source[0])
+            self.update_help_changes()
+
+    def helplist_item_edit(self):
+        """Handle edit button for the help list.
+
+        Query with existing help source information and update
+        config if the values are changed.
+        """
+        item_index = self.helplist.index(ANCHOR)
+        help_source = self.user_helplist[item_index]
+        new_help_source = HelpSource(
+                self, 'Edit Help Source',
+                menuitem=help_source[0],
+                filepath=help_source[1],
+                ).result
+        if new_help_source and new_help_source != help_source:
+            self.user_helplist[item_index] = new_help_source
+            self.helplist.delete(item_index)
+            self.helplist.insert(item_index, new_help_source[0])
+            self.update_help_changes()
+            self.set_add_delete_state()  # Selected will be un-selected
+
+    def helplist_item_remove(self):
+        """Handle remove button for the help list.
+
+        Delete the help list item from config.
+        """
+        item_index = self.helplist.index(ANCHOR)
+        del(self.user_helplist[item_index])
+        self.helplist.delete(item_index)
+        self.update_help_changes()
+        self.set_add_delete_state()
+
+    def update_help_changes(self):
+        "Clear and rebuild the HelpFiles section in changes"
+        changes['main']['HelpFiles'] = {}
+        for num in range(1, len(self.user_helplist) + 1):
+            changes.add_option(
+                    'main', 'HelpFiles', str(num),
+                    ';'.join(self.user_helplist[num-1][:2]))
+
+
+class VarTrace:
+    """Maintain Tk variables trace state."""
+
+    def __init__(self):
+        """Store Tk variables and callbacks.
+
+        untraced: List of tuples (var, callback)
+            that do not have the callback attached
+            to the Tk var.
+        traced: List of tuples (var, callback) where
+            that callback has been attached to the var.
+        """
+        self.untraced = []
+        self.traced = []
+
+    def clear(self):
+        "Clear lists (for tests)."
+        # Call after all tests in a module to avoid memory leaks.
+        self.untraced.clear()
+        self.traced.clear()
+
+    def add(self, var, callback):
+        """Add (var, callback) tuple to untraced list.
+
+        Args:
+            var: Tk variable instance.
+            callback: Either function name to be used as a callback
+                or a tuple with IdleConf config-type, section, and
+                option names used in the default callback.
+
+        Return:
+            Tk variable instance.
+        """
+        if isinstance(callback, tuple):
+            callback = self.make_callback(var, callback)
+        self.untraced.append((var, callback))
+        return var
+
+    @staticmethod
+    def make_callback(var, config):
+        "Return default callback function to add values to changes instance."
+        def default_callback(*params):
+            "Add config values to changes instance."
+            changes.add_option(*config, var.get())
+        return default_callback
+
+    def attach(self):
+        "Attach callback to all vars that are not traced."
+        while self.untraced:
+            var, callback = self.untraced.pop()
+            var.trace_add('write', callback)
+            self.traced.append((var, callback))
+
+    def detach(self):
+        "Remove callback from traced vars."
+        while self.traced:
+            var, callback = self.traced.pop()
+            var.trace_remove('write', var.trace_info()[0][1])
+            self.untraced.append((var, callback))
+
+
+tracers = VarTrace()
+
 help_common = '''\
 When you click either the Apply or Ok buttons, settings in this
 dialog that are different from IDLE's default are saved in
@@ -1390,7 +2112,7 @@ machine. Some do not take affect until IDLE is restarted.
 [Cancel] only cancels changes made since the last save.
 '''
 help_pages = {
-    'Highlighting': '''
+    'Highlights': '''
 Highlighting:
 The IDLE Dark color theme is new in October 2015.  It can only
 be used with older IDLE releases if it is saved as a custom
@@ -1402,6 +2124,21 @@ The IDLE Modern Unix key set is new in June 2016.  It can only
 be used with older IDLE releases if it is saved as a custom
 key set, with a different name.
 ''',
+     'General': '''
+General:
+
+AutoComplete: Popupwait is milleseconds to wait after key char, without
+cursor movement, before popping up completion box.  Key char is '.' after
+identifier or a '/' (or '\\' on Windows) within a string.
+
+FormatParagraph: Max-width is max chars in lines after re-formatting.
+Use with paragraphs in both strings and comment blocks.
+
+ParenMatch: Style indicates what is highlighted when closer is entered:
+'opener' - opener '({[' corresponding to closer; 'parens' - both chars;
+'expression' (default) - also everything in between.  Flash-delay is how
+long to highlight if cursor is not moved (0 means forever).
+'''
 }
 
 
@@ -1426,33 +2163,33 @@ class VerticalScrolledFrame(Frame):
     def __init__(self, parent, *args, **kw):
         Frame.__init__(self, parent, *args, **kw)
 
-        # create a canvas object and a vertical scrollbar for scrolling it
+        # Create a canvas object and a vertical scrollbar for scrolling it.
         vscrollbar = Scrollbar(self, orient=VERTICAL)
         vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
-        canvas = Canvas(self, bd=0, highlightthickness=0,
+        canvas = Canvas(self, borderwidth=0, highlightthickness=0,
                         yscrollcommand=vscrollbar.set, width=240)
         canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
         vscrollbar.config(command=canvas.yview)
 
-        # reset the view
+        # Reset the view.
         canvas.xview_moveto(0)
         canvas.yview_moveto(0)
 
-        # create a frame inside the canvas which will be scrolled with it
+        # Create a frame inside the canvas which will be scrolled with it.
         self.interior = interior = Frame(canvas)
         interior_id = canvas.create_window(0, 0, window=interior, anchor=NW)
 
-        # track changes to the canvas and frame width and sync them,
-        # also updating the scrollbar
+        # Track changes to the canvas and frame width and sync them,
+        # also updating the scrollbar.
         def _configure_interior(event):
-            # update the scrollbars to match the size of the inner frame
+            # Update the scrollbars to match the size of the inner frame.
             size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
             canvas.config(scrollregion="0 0 %s %s" % size)
         interior.bind('<Configure>', _configure_interior)
 
         def _configure_canvas(event):
             if interior.winfo_reqwidth() != canvas.winfo_width():
-                # update the inner frame's width to fill the canvas
+                # Update the inner frame's width to fill the canvas.
                 canvas.itemconfigure(interior_id, width=canvas.winfo_width())
         canvas.bind('<Configure>', _configure_canvas)
 
index ab4f1a3..855d375 100644 (file)
@@ -1,4 +1,3 @@
-import importlib
 import importlib.abc
 import importlib.util
 import os
@@ -32,7 +31,6 @@ from idlelib import windows
 TK_TABWIDTH_DEFAULT = 8
 _py_version = ' (%s)' % platform.python_version()
 
-
 def _sphinx_version():
     "Format sys.version_info to produce the Sphinx version string used to install the chm docs"
     major, minor, micro, level, serial = sys.version_info
@@ -53,11 +51,22 @@ class EditorWindow(object):
     from idlelib import mainmenu
     from tkinter import Toplevel
     from idlelib.statusbar import MultiStatusBar
+    from idlelib.autocomplete import AutoComplete
+    from idlelib.autoexpand import AutoExpand
+    from idlelib.calltips import CallTips
+    from idlelib.codecontext import CodeContext
+    from idlelib.paragraph import FormatParagraph
+    from idlelib.parenmatch import ParenMatch
+    from idlelib.rstrip import RstripExtension
+    from idlelib.zoomheight import ZoomHeight
 
     filesystemencoding = sys.getfilesystemencoding()  # for file names
     help_url = None
 
     def __init__(self, flist=None, filename=None, key=None, root=None):
+        # Delay import: runscript imports pyshell imports EditorWindow.
+        from idlelib.runscript import ScriptBinding
+
         if EditorWindow.help_url is None:
             dochome =  os.path.join(sys.base_prefix, 'Doc', 'index.html')
             if sys.platform.count('linux'):
@@ -85,7 +94,8 @@ class EditorWindow(object):
                     # Safari requires real file:-URLs
                     EditorWindow.help_url = 'file://' + EditorWindow.help_url
             else:
-                EditorWindow.help_url = "https://docs.python.org/%d.%d/" % sys.version_info[:2]
+                EditorWindow.help_url = ("https://docs.python.org/%d.%d/"
+                                         % sys.version_info[:2])
         self.flist = flist
         root = root or flist.root
         self.root = root
@@ -104,8 +114,8 @@ class EditorWindow(object):
             self.tkinter_vars = {}  # keys: Tkinter event names
                                     # values: Tkinter variable instances
             self.top.instance_dict = {}
-        self.recent_files_path = os.path.join(idleConf.GetUserCfgDir(),
-                'recent-files.lst')
+        self.recent_files_path = os.path.join(
+                idleConf.userdir, 'recent-files.lst')
         self.text_frame = text_frame = Frame(top)
         self.vbar = vbar = Scrollbar(text_frame, name='vbar')
         self.width = idleConf.GetOption('main', 'EditorWindow',
@@ -147,7 +157,7 @@ class EditorWindow(object):
         text.bind("<<python-docs>>", self.python_docs)
         text.bind("<<about-idle>>", self.about_dialog)
         text.bind("<<open-config-dialog>>", self.config_dialog)
-        text.bind("<<open-module>>", self.open_module)
+        text.bind("<<open-module>>", self.open_module_event)
         text.bind("<<do-nothing>>", lambda event: "break")
         text.bind("<<select-all>>", self.select_all)
         text.bind("<<remove-selection>>", self.remove_selection)
@@ -271,6 +281,43 @@ class EditorWindow(object):
         self.askinteger = tkSimpleDialog.askinteger
         self.showerror = tkMessageBox.showerror
 
+        # Add pseudoevents for former extension fixed keys.
+        # (This probably needs to be done once in the process.)
+        text.event_add('<<autocomplete>>', '<Key-Tab>')
+        text.event_add('<<try-open-completions>>', '<KeyRelease-period>',
+                       '<KeyRelease-slash>', '<KeyRelease-backslash>')
+        text.event_add('<<try-open-calltip>>', '<KeyRelease-parenleft>')
+        text.event_add('<<refresh-calltip>>', '<KeyRelease-parenright>')
+        text.event_add('<<paren-closed>>', '<KeyRelease-parenright>',
+                       '<KeyRelease-bracketright>', '<KeyRelease-braceright>')
+
+        # Former extension bindings depends on frame.text being packed
+        # (called from self.ResetColorizer()).
+        autocomplete = self.AutoComplete(self)
+        text.bind("<<autocomplete>>", autocomplete.autocomplete_event)
+        text.bind("<<try-open-completions>>",
+                  autocomplete.try_open_completions_event)
+        text.bind("<<force-open-completions>>",
+                  autocomplete.force_open_completions_event)
+        text.bind("<<expand-word>>", self.AutoExpand(self).expand_word_event)
+        text.bind("<<format-paragraph>>",
+                  self.FormatParagraph(self).format_paragraph_event)
+        parenmatch = self.ParenMatch(self)
+        text.bind("<<flash-paren>>", parenmatch.flash_paren_event)
+        text.bind("<<paren-closed>>", parenmatch.paren_closed_event)
+        scriptbinding = ScriptBinding(self)
+        text.bind("<<check-module>>", scriptbinding.check_module_event)
+        text.bind("<<run-module>>", scriptbinding.run_module_event)
+        text.bind("<<do-rstrip>>", self.RstripExtension(self).do_rstrip)
+        calltips = self.CallTips(self)
+        text.bind("<<try-open-calltip>>", calltips.try_open_calltip_event)
+        #refresh-calltips must come after paren-closed to work right
+        text.bind("<<refresh-calltip>>", calltips.refresh_calltip_event)
+        text.bind("<<force-open-calltip>>", calltips.force_open_calltip_event)
+        text.bind("<<zoom-height>>", self.ZoomHeight(self).zoom_height_event)
+        text.bind("<<toggle-code-context>>",
+                  self.CodeContext(self).toggle_code_context_event)
+
     def _filename_to_unicode(self, filename):
         """Return filename as BMP unicode so diplayable in Tk."""
         # Decode bytes to unicode.
@@ -294,7 +341,7 @@ class EditorWindow(object):
     def home_callback(self, event):
         if (event.state & 4) != 0 and event.keysym == "Home":
             # state&4==Control. If <Control-Home>, use the Tk binding.
-            return
+            return None
         if self.text.index("iomark") and \
            self.text.compare("iomark", "<=", "insert lineend") and \
            self.text.compare("insert linestart", "<=", "iomark"):
@@ -423,6 +470,7 @@ class EditorWindow(object):
         rmenu.tk_popup(event.x_root, event.y_root)
         if iswin:
             self.text.config(cursor="ibeam")
+        return "break"
 
     rmenu_specs = [
         # ("Label", "<<virtual-event>>", "statefuncname"), ...
@@ -463,12 +511,14 @@ class EditorWindow(object):
     def about_dialog(self, event=None):
         "Handle Help 'About IDLE' event."
         # Synchronize with macosx.overrideRootMenu.about_dialog.
-        help_about.AboutDialog(self.top,'About IDLE')
+        help_about.AboutDialog(self.top)
+        return "break"
 
     def config_dialog(self, event=None):
         "Handle Options 'Configure IDLE' event."
         # Synchronize with macosx.overrideRootMenu.config_dialog.
         configdialog.ConfigDialog(self.top,'Settings')
+        return "break"
 
     def help_dialog(self, event=None):
         "Handle Help 'IDLE Help' event."
@@ -478,6 +528,7 @@ class EditorWindow(object):
         else:
             parent = self.top
         help.show_idlehelp(parent)
+        return "break"
 
     def python_docs(self, event=None):
         if sys.platform[:3] == 'win':
@@ -497,7 +548,7 @@ class EditorWindow(object):
     def copy(self,event):
         if not self.text.tag_ranges("sel"):
             # There is no selection, so do nothing and maybe interrupt.
-            return
+            return None
         self.text.event_generate("<<Copy>>")
         return "break"
 
@@ -515,6 +566,7 @@ class EditorWindow(object):
     def remove_selection(self, event=None):
         self.text.tag_remove("sel", "1.0", "end")
         self.text.see("insert")
+        return "break"
 
     def move_at_edge_if_selection(self, edge_index):
         """Cursor move begins at start or end of selection
@@ -575,8 +627,9 @@ class EditorWindow(object):
             return "break"
         text.mark_set("insert", "%d.0" % lineno)
         text.see("insert")
+        return "break"
 
-    def open_module(self, event=None):
+    def open_module(self):
         """Get module name from user and open it.
 
         Return module path or None for calls by open_class_browser
@@ -600,21 +653,27 @@ class EditorWindow(object):
                 self.io.loadfile(file_path)
         return file_path
 
+    def open_module_event(self, event):
+        self.open_module()
+        return "break"
+
     def open_class_browser(self, event=None):
         filename = self.io.filename
         if not (self.__class__.__name__ == 'PyShellEditorWindow'
                 and filename):
             filename = self.open_module()
             if filename is None:
-                return
+                return "break"
         head, tail = os.path.split(filename)
         base, ext = os.path.splitext(tail)
         from idlelib import browser
         browser.ClassBrowser(self.flist, base, [head])
+        return "break"
 
     def open_path_browser(self, event=None):
         from idlelib import pathbrowser
         pathbrowser.PathBrowser(self.flist)
+        return "break"
 
     def open_turtle_demo(self, event = None):
         import subprocess
@@ -623,6 +682,7 @@ class EditorWindow(object):
                '-c',
                'from turtledemo.__main__ import main; main()']
         subprocess.Popen(cmd, shell=False)
+        return "break"
 
     def gotoline(self, lineno):
         if lineno is not None and lineno > 0:
@@ -879,6 +939,7 @@ class EditorWindow(object):
 
     def center_insert_event(self, event):
         self.center()
+        return "break"
 
     def center(self, mark="insert"):
         text = self.text
@@ -910,6 +971,7 @@ class EditorWindow(object):
 
     def close_event(self, event):
         self.close()
+        return "break"
 
     def maybesave(self):
         if self.io:
@@ -967,16 +1029,8 @@ class EditorWindow(object):
     def get_standard_extension_names(self):
         return idleConf.GetExtensions(editor_only=True)
 
-    extfiles = {  # map config-extension section names to new file names
-        'AutoComplete': 'autocomplete',
-        'AutoExpand': 'autoexpand',
-        'CallTips': 'calltips',
-        'CodeContext': 'codecontext',
-        'FormatParagraph': 'paragraph',
-        'ParenMatch': 'parenmatch',
-        'RstripExtension': 'rstrip',
-        'ScriptBinding': 'runscript',
-        'ZoomHeight': 'zoomheight',
+    extfiles = {  # Map built-in config-extension section names to file names.
+        'ZzDummy': 'zzdummy',
         }
 
     def load_extension(self, name):
@@ -1357,6 +1411,7 @@ class EditorWindow(object):
             line = lines[pos]
             lines[pos] = '##' + line
         self.set_region(head, tail, chars, lines)
+        return "break"
 
     def uncomment_region_event(self, event):
         head, tail, chars, lines = self.get_region()
@@ -1370,6 +1425,7 @@ class EditorWindow(object):
                 line = line[1:]
             lines[pos] = line
         self.set_region(head, tail, chars, lines)
+        return "break"
 
     def tabify_region_event(self, event):
         head, tail, chars, lines = self.get_region()
@@ -1382,6 +1438,7 @@ class EditorWindow(object):
                 ntabs, nspaces = divmod(effective, tabwidth)
                 lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:]
         self.set_region(head, tail, chars, lines)
+        return "break"
 
     def untabify_region_event(self, event):
         head, tail, chars, lines = self.get_region()
@@ -1390,6 +1447,7 @@ class EditorWindow(object):
         for pos in range(len(lines)):
             lines[pos] = lines[pos].expandtabs(tabwidth)
         self.set_region(head, tail, chars, lines)
+        return "break"
 
     def toggle_tabs_event(self, event):
         if self.askyesno(
index 64ba28d..c55c48c 100644 (file)
@@ -1,3 +1,8 @@
+"""Grep dialog for Find in Files functionality.
+
+   Inherits from SearchDialogBase for GUI and uses searchengine
+   to prepare search pattern.
+"""
 import fnmatch
 import os
 import sys
@@ -11,7 +16,17 @@ from idlelib import searchengine
 # Importing OutputWindow here fails due to import loop
 # EditorWindow -> GrepDialop -> OutputWindow -> EditorWindow
 
+
 def grep(text, io=None, flist=None):
+    """Create or find singleton GrepDialog instance.
+
+    Args:
+        text: Text widget that contains the selected text for
+              default search phrase.
+        io: iomenu.IOBinding instance with default path to search.
+        flist: filelist.FileList instance for OutputWindow parent.
+    """
+
     root = text._root()
     engine = searchengine.get(root)
     if not hasattr(engine, "_grepdialog"):
@@ -20,19 +35,32 @@ def grep(text, io=None, flist=None):
     searchphrase = text.get("sel.first", "sel.last")
     dialog.open(text, searchphrase, io)
 
+
 class GrepDialog(SearchDialogBase):
+    "Dialog for searching multiple files."
 
     title = "Find in Files Dialog"
     icon = "Grep"
     needwrapbutton = 0
 
     def __init__(self, root, engine, flist):
+        """Create search dialog for searching for a phrase in the file system.
+
+        Uses SearchDialogBase as the basis for the GUI and a
+        searchengine instance to prepare the search.
+
+        Attributes:
+            globvar: Value of Text Entry widget for path to search.
+            recvar: Boolean value of Checkbutton widget
+                    for traversing through subdirectories.
+        """
         SearchDialogBase.__init__(self, root, engine)
         self.flist = flist
         self.globvar = StringVar(root)
         self.recvar = BooleanVar(root)
 
     def open(self, text, searchphrase, io=None):
+        "Make dialog visible on top of others and ready to use."
         SearchDialogBase.open(self, text, searchphrase)
         if io:
             path = io.filename or ""
@@ -45,20 +73,30 @@ class GrepDialog(SearchDialogBase):
         self.globvar.set(os.path.join(dir, "*" + tail))
 
     def create_entries(self):
+        "Create base entry widgets and add widget for search path."
         SearchDialogBase.create_entries(self)
         self.globent = self.make_entry("In files:", self.globvar)[0]
 
     def create_other_buttons(self):
+        "Add check button to recurse down subdirectories."
         btn = Checkbutton(
                 self.make_frame()[0], variable=self.recvar,
                 text="Recurse down subdirectories")
         btn.pack(side="top", fill="both")
 
     def create_command_buttons(self):
+        "Create base command buttons and add button for search."
         SearchDialogBase.create_command_buttons(self)
         self.make_button("Search Files", self.default_command, 1)
 
     def default_command(self, event=None):
+        """Grep for search pattern in file path. The default command is bound
+        to <Return>.
+
+        If entry values are populated, set OutputWindow as stdout
+        and perform search.  The search dialog is closed automatically
+        when the search begins.
+        """
         prog = self.engine.getprog()
         if not prog:
             return
@@ -75,12 +113,19 @@ class GrepDialog(SearchDialogBase):
             sys.stdout = save
 
     def grep_it(self, prog, path):
+        """Search for prog within the lines of the files in path.
+
+        For the each file in the path directory, open the file and
+        search each line for the matching pattern.  If the pattern is
+        found,  write the file and line information to stdout (which
+        is an OutputWindow).
+        """
         dir, base = os.path.split(path)
         list = self.findfiles(dir, base, self.recvar.get())
         list.sort()
         self.close()
         pat = self.engine.getpat()
-        print("Searching %r in %s ..." % (pat, path))
+        print(f"Searching {pat!r} in {path} ...")
         hits = 0
         try:
             for fn in list:
@@ -90,20 +135,22 @@ class GrepDialog(SearchDialogBase):
                             if line[-1:] == '\n':
                                 line = line[:-1]
                             if prog.search(line):
-                                sys.stdout.write("%s: %s: %s\n" %
-                                                 (fn, lineno, line))
+                                sys.stdout.write(f"{fn}: {lineno}: {line}\n")
                                 hits += 1
                 except OSError as msg:
                     print(msg)
-            print(("Hits found: %s\n"
-                  "(Hint: right-click to open locations.)"
-                  % hits) if hits else "No hits.")
+            print(f"Hits found: {hits}\n(Hint: right-click to open locations.)"
+                  if hits else "No hits.")
         except AttributeError:
             # Tk window has been closed, OutputWindow.text = None,
             # so in OW.write, OW.text.insert fails.
             pass
 
     def findfiles(self, dir, base, rec):
+        """Return list of files in the dir that match the base pattern.
+
+        If rec is True, recursively iterate through subdirectories.
+        """
         try:
             names = os.listdir(dir or os.curdir)
         except OSError as msg:
@@ -123,11 +170,6 @@ class GrepDialog(SearchDialogBase):
                 list.extend(self.findfiles(subdir, base, rec))
         return list
 
-    def close(self, event=None):
-        if self.top:
-            self.top.grab_release()
-            self.top.withdraw()
-
 
 def _grep_dialog(parent):  # htest #
     from tkinter import Toplevel, Text, SEL, END
@@ -136,7 +178,7 @@ def _grep_dialog(parent):  # htest #
     top = Toplevel(parent)
     top.title("Test GrepDialog")
     x, y = map(int, parent.geometry().split('+')[1:])
-    top.geometry("+%d+%d" % (x, y + 175))
+    top.geometry(f"+{x}+{y + 175}")
 
     flist = PyShellFileList(top)
     text = Text(top, height=5)
index 0a3062e..33ae884 100644 (file)
@@ -227,7 +227,9 @@ community is 4 spaces.</dd>
 multiline string or selected line in a string.  All lines in the
 paragraph will be formatted to less than N columns, where N defaults to 72.</dd>
 <dt>Strip trailing whitespace</dt>
-<dd>Remove any space characters after the last non-space character of a line.</dd>
+<dd>Remove trailing space and other whitespace characters after the last
+non-whitespace character of a line by applying str.rstrip to each line,
+including lines within multiline strings.</dd>
 </dl>
 </div>
 <div class="section" id="run-menu-editor-window-only">
@@ -573,14 +575,14 @@ starting from a console (<code class="docutils literal"><span class="pre">python
 </div>
 <div class="section" id="idle-console-differences">
 <h3>25.5.3.3. IDLE-console differences<a class="headerlink" href="#idle-console-differences" title="Permalink to this headline">¶</a></h3>
-<p>As much as possible, the result of executing Python code with IDLE is the
-same as executing the same code in a console window.  However, the different
-interface and operation occasionally affect visible results.  For instance,
-<code class="docutils literal"><span class="pre">sys.modules</span></code> starts with more entries.</p>
+<p>With rare exceptions, the result of executing Python code with IDLE is
+intended to be the same as executing the same code in a console window.
+However, the different interface and operation occasionally affect
+visible results.  For instance, <code class="docutils literal"><span class="pre">sys.modules</span></code> starts with more entries.</p>
 <p>IDLE also replaces <code class="docutils literal"><span class="pre">sys.stdin</span></code>, <code class="docutils literal"><span class="pre">sys.stdout</span></code>, and <code class="docutils literal"><span class="pre">sys.stderr</span></code> with
 objects that get input from and send output to the Shell window.
-When this window has the focus, it controls the keyboard and screen.
-This is normally transparent, but functions that directly access the keyboard
+When Shell has the focus, it controls the keyboard and screen.  This is
+normally transparent, but functions that directly access the keyboard
 and screen will not work.  If <code class="docutils literal"><span class="pre">sys</span></code> is reset with <code class="docutils literal"><span class="pre">importlib.reload(sys)</span></code>,
 IDLE&#8217;s changes are lost and things like <code class="docutils literal"><span class="pre">input</span></code>, <code class="docutils literal"><span class="pre">raw_input</span></code>, and
 <code class="docutils literal"><span class="pre">print</span></code> will not work correctly.</p>
@@ -589,8 +591,28 @@ Some consoles only work with a single physical line at a time.  IDLE uses
 <code class="docutils literal"><span class="pre">exec</span></code> to run each statement.  As a result, <code class="docutils literal"><span class="pre">'__builtins__'</span></code> is always
 defined for each statement.</p>
 </div>
+<div class="section" id="developing-tkinter-applications">
+<h3>25.5.3.4. Developing tkinter applications<a class="headerlink" href="#developing-tkinter-applications" title="Permalink to this headline">¶</a></h3>
+<p>IDLE is intentionally different from standard Python in order to
+facilitate development of tkinter programs.  Enter <code class="docutils literal"><span class="pre">import</span> <span class="pre">tkinter</span> <span class="pre">as</span> <span class="pre">tk;</span>
+<span class="pre">root</span> <span class="pre">=</span> <span class="pre">tk.Tk()</span></code> in standard Python and nothing appears.  Enter the same
+in IDLE and a tk window appears.  In standard Python, one must also enter
+<code class="docutils literal"><span class="pre">root.update()</span></code> to see the window.  IDLE does the equivalent in the
+background, about 20 times a second, which is about every 50 milleseconds.
+Next enter <code class="docutils literal"><span class="pre">b</span> <span class="pre">=</span> <span class="pre">tk.Button(root,</span> <span class="pre">text='button');</span> <span class="pre">b.pack()</span></code>.  Again,
+nothing visibly changes in standard Python until one enters <code class="docutils literal"><span class="pre">root.update()</span></code>.</p>
+<p>Most tkinter programs run <code class="docutils literal"><span class="pre">root.mainloop()</span></code>, which usually does not
+return until the tk app is destroyed.  If the program is run with
+<code class="docutils literal"><span class="pre">python</span> <span class="pre">-i</span></code> or from an IDLE editor, a <code class="docutils literal"><span class="pre">&gt;&gt;&gt;</span></code> shell prompt does not
+appear until <code class="docutils literal"><span class="pre">mainloop()</span></code> returns, at which time there is nothing left
+to interact with.</p>
+<p>When running a tkinter program from an IDLE editor, one can comment out
+the mainloop call.  One then gets a shell prompt immediately and can
+interact with the live application.  One just has to remember to
+re-enable the mainloop call when running in standard Python.</p>
+</div>
 <div class="section" id="running-without-a-subprocess">
-<h3>25.5.3.4. Running without a subprocess<a class="headerlink" href="#running-without-a-subprocess" title="Permalink to this headline">¶</a></h3>
+<h3>25.5.3.5. Running without a subprocess<a class="headerlink" href="#running-without-a-subprocess" title="Permalink to this headline">¶</a></h3>
 <p>By default, IDLE executes user code in a separate subprocess via a socket,
 which uses the internal loopback interface.  This connection is not
 externally visible and no data is sent to or received from the Internet.
@@ -635,20 +657,10 @@ custom key set in the Configure IDLE dialog under the keys tab.</p>
 <div class="section" id="extensions">
 <h3>25.5.4.3. Extensions<a class="headerlink" href="#extensions" title="Permalink to this headline">¶</a></h3>
 <p>IDLE contains an extension facility.  Preferences for extensions can be
-changed with Configure Extensions. See the beginning of config-extensions.def
-in the idlelib directory for further information.  The default extensions
-are currently:</p>
-<ul class="simple">
-<li>FormatParagraph</li>
-<li>AutoExpand</li>
-<li>ZoomHeight</li>
-<li>ScriptBinding</li>
-<li>CallTips</li>
-<li>ParenMatch</li>
-<li>AutoComplete</li>
-<li>CodeContext</li>
-<li>RstripExtension</li>
-</ul>
+changed with the Extensions tab of the preferences dialog. See the
+beginning of config-extensions.def in the idlelib directory for further
+information.  The only current default extension is zzdummy, an example
+also used for testing.</p>
 </div>
 </div>
 </div>
@@ -687,7 +699,8 @@ are currently:</p>
 <li><a class="reference internal" href="#command-line-usage">25.5.3.1. Command line usage</a></li>
 <li><a class="reference internal" href="#startup-failure">25.5.3.2. Startup failure</a></li>
 <li><a class="reference internal" href="#idle-console-differences">25.5.3.3. IDLE-console differences</a></li>
-<li><a class="reference internal" href="#running-without-a-subprocess">25.5.3.4. Running without a subprocess</a></li>
+<li><a class="reference internal" href="#developing-tkinter-applications">25.5.3.4. Developing tkinter applications</a></li>
+<li><a class="reference internal" href="#running-without-a-subprocess">25.5.3.5. Running without a subprocess</a></li>
 </ul>
 </li>
 <li><a class="reference internal" href="#help-and-preferences">25.5.4. Help and preferences</a><ul>
@@ -711,7 +724,7 @@ are currently:</p>
     <ul class="this-page-menu">
       <li><a href="../bugs.html">Report a Bug</a></li>
       <li>
-        <a href="https://github.com/python/cpython/blob/master/Doc/library/idle.rst"
+        <a href="https://github.com/python/cpython/blob/master/Doc/library/idle.txt"
             rel="nofollow">Show Source
         </a>
       </li>
@@ -768,7 +781,7 @@ are currently:</p>
     The Python Software Foundation is a non-profit corporation.
     <a href="https://www.python.org/psf/donations/">Please donate.</a>
     <br />
-    Last updated on Jun 13, 2017.
+    Last updated on Sep 15, 2017.
     <a href="../bugs.html">Found a bug</a>?
     <br />
     Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.3.6.
index f0a40e9..679ac78 100644 (file)
@@ -2,19 +2,28 @@
 
 """
 import os
-from sys import version
+import sys
+from platform import python_version, architecture
 
-from tkinter import Toplevel, Frame, Label, Button
-from tkinter import SUNKEN, TOP, BOTTOM, LEFT, X, BOTH, W, EW, NSEW
+from tkinter import Toplevel, Frame, Label, Button, PhotoImage
+from tkinter import SUNKEN, TOP, BOTTOM, LEFT, X, BOTH, W, EW, NSEW, E
 
 from idlelib import textview
 
 
+def build_bits():
+    "Return bits for platform."
+    if sys.platform == 'darwin':
+        return '64' if sys.maxsize > 2**32 else '32'
+    else:
+        return architecture()[0][:2]
+
+
 class AboutDialog(Toplevel):
     """Modal about dialog for idle
 
     """
-    def __init__(self, parent, title, _htest=False, _utest=False):
+    def __init__(self, parent, title=None, _htest=False, _utest=False):
         """Create popup, do not return until tk widget destroyed.
 
         parent - parent of this dialog
@@ -28,11 +37,12 @@ class AboutDialog(Toplevel):
         self.geometry("+%d+%d" % (
                         parent.winfo_rootx()+30,
                         parent.winfo_rooty()+(30 if not _htest else 100)))
-        self.bg = "#707070"
-        self.fg = "#ffffff"
+        self.bg = "#bbbbbb"
+        self.fg = "#000000"
         self.create_widgets()
         self.resizable(height=False, width=False)
-        self.title(title)
+        self.title(title or
+                   f'About IDLE {python_version()} ({build_bits()} bit)')
         self.transient(parent)
         self.grab_set()
         self.protocol("WM_DELETE_WINDOW", self.ok)
@@ -48,7 +58,6 @@ class AboutDialog(Toplevel):
             self.wait_window()
 
     def create_widgets(self):
-        release = version[:version.index(' ')]
         frame = Frame(self, borderwidth=2, relief=SUNKEN)
         frame_buttons = Frame(self)
         frame_buttons.pack(side=BOTTOM, fill=X)
@@ -62,8 +71,17 @@ class AboutDialog(Toplevel):
 
         header = Label(frame_background, text='IDLE', fg=self.fg,
                        bg=self.bg, font=('courier', 24, 'bold'))
-        header.grid(row=0, column=0, sticky=W, padx=10, pady=10)
-        byline_text = "Python's Integrated DeveLopment Environment" + 5*'\n'
+        header.grid(row=0, column=0, sticky=E, padx=10, pady=10)
+
+        tk_patchlevel = self.tk.call('info', 'patchlevel')
+        ext = '.png' if tk_patchlevel >= '8.6' else '.gif'
+        icon = os.path.join(os.path.abspath(os.path.dirname(__file__)),
+                            'Icons', f'idle_48{ext}')
+        self.icon_image = PhotoImage(master=self._root(), file=icon)
+        logo = Label(frame_background, image=self.icon_image, bg=self.bg)
+        logo.grid(row=0, column=0, sticky=W, rowspan=2, padx=10, pady=10)
+
+        byline_text = "Python's Integrated Development\nand Learning Environment" + 5*'\n'
         byline = Label(frame_background, text=byline_text, justify=LEFT,
                        fg=self.fg, bg=self.bg)
         byline.grid(row=2, column=0, sticky=W, columnspan=3, padx=10, pady=5)
@@ -71,7 +89,7 @@ class AboutDialog(Toplevel):
                       justify=LEFT, fg=self.fg, bg=self.bg)
         email.grid(row=6, column=0, columnspan=2, sticky=W, padx=10, pady=0)
         docs = Label(frame_background, text='https://docs.python.org/' +
-                     version[:3] + '/library/idle.html',
+                     python_version()[:3] + '/library/idle.html',
                      justify=LEFT, fg=self.fg, bg=self.bg)
         docs.grid(row=7, column=0, columnspan=2, sticky=W, padx=10, pady=0)
 
@@ -79,10 +97,10 @@ class AboutDialog(Toplevel):
               height=2, bg=self.bg).grid(row=8, column=0, sticky=EW,
                                          columnspan=3, padx=5, pady=5)
 
-        pyver = Label(frame_background, text='Python version:  ' + release,
+        pyver = Label(frame_background,
+                      text='Python version:  ' + python_version(),
                       fg=self.fg, bg=self.bg)
         pyver.grid(row=9, column=0, sticky=W, padx=10, pady=0)
-        tk_patchlevel = self.tk.call('info', 'patchlevel')
         tkver = Label(frame_background, text='Tk version:  ' + tk_patchlevel,
                       fg=self.fg, bg=self.bg)
         tkver.grid(row=9, column=1, sticky=W, padx=2, pady=0)
@@ -105,7 +123,8 @@ class AboutDialog(Toplevel):
               height=2, bg=self.bg).grid(row=11, column=0, sticky=EW,
                                          columnspan=3, padx=5, pady=5)
 
-        idlever = Label(frame_background, text='IDLE version:   ' + release,
+        idlever = Label(frame_background,
+                        text='IDLE version:   ' + python_version(),
                         fg=self.fg, bg=self.bg)
         idlever.grid(row=12, column=0, sticky=W, padx=10, pady=0)
         idle_buttons = Frame(frame_background, bg=self.bg)
index dc7a286..c580fb9 100644 (file)
@@ -159,3 +159,59 @@ complete, though some tests need improvement. To run all htests, run the
 htest file from an editor or from the command line with:
 
 python -m idlelib.idle_test.htest
+
+
+5. Test Coverage
+
+Install the coverage package into your Python 3.6 site-packages
+directory.  (Its exact location depends on the OS).
+> python3 -m pip install coverage
+(On Windows, replace 'python3 with 'py -3.6' or perhaps just 'python'.)
+
+The problem with running coverage with repository python is that
+coverage uses absolute imports for its submodules, hence it needs to be
+in a directory in sys.path.  One solution: copy the package to the
+directory containing the cpython repository.  Call it 'dev'.  Then run
+coverage either directly or from a script in that directory so that
+'dev' is prepended to sys.path.
+
+Either edit or add dev/.coveragerc so it looks something like this.
+---
+# .coveragerc sets coverage options.
+[run]
+branch = True
+
+[report]
+# Regexes for lines to exclude from consideration
+exclude_lines =
+    # Don't complain if non-runnable code isn't run:
+    if 0:
+    if __name__ == .__main__.:
+
+    .*# htest #
+    if not _utest:
+    if _htest:
+---
+The additions for IDLE are 'branch = True', to test coverage both ways,
+and the last three exclude lines, to exclude things peculiar to IDLE
+that are not executed during tests.
+
+A script like the following cover.bat (for Windows) is very handy.
+---
+@echo off
+rem Usage: cover filename [test_ suffix] # proper case required by coverage
+rem filename without .py, 2nd parameter if test is not test_filename
+setlocal
+set py=f:\dev\3x\pcbuild\win32\python_d.exe
+set src=idlelib.%1
+if "%2" EQU "" set tst=f:/dev/3x/Lib/idlelib/idle_test/test_%1.py
+if "%2" NEQ "" set tst=f:/dev/ex/Lib/idlelib/idle_test/test_%2.py
+
+%py% -m coverage run --pylib --source=%src% %tst%
+%py% -m coverage report --show-missing
+%py% -m coverage html
+start htmlcov\3x_Lib_idlelib_%1_py.html
+rem Above opens new report; htmlcov\index.html displays report index
+---
+The second parameter was added for tests of module x not named test_x.
+(There were several before modules were renamed, now only one is left.)
index 6f676ae..e483bbc 100644 (file)
@@ -8,7 +8,7 @@ callable in the module named in the spec.  Close the window to skip or
 end the test.
 
 In a tested module, let X be a global name bound to a callable (class
-or function) whose .__name__ attrubute is also X (the usual situation).
+or function) whose .__name__ attribute is also X (the usual situation).
 The first parameter of X must be 'parent'.  When called, the parent
 argument will be the root window.  X must create a child Toplevel
 window (or subclass thereof).  The Toplevel may be a test widget or
@@ -306,15 +306,6 @@ _tabbed_pages_spec = {
            "<nothing> is an invalid add page and remove page name.\n"
     }
 
-TextViewer_spec = {
-    'file': 'textview',
-    'kwds': {'title': 'Test textview',
-             'text':'The quick brown fox jumps over the lazy dog.\n'*35,
-             '_htest': True},
-    'msg': "Test for read-only property of text.\n"
-           "Text is selectable. Window is scrollable.",
-     }
-
 _tooltip_spec = {
     'file': 'tooltip',
     'kwds': {},
@@ -338,6 +329,15 @@ _undo_delegator_spec = {
            "by printing to the console or the IDLE shell.\n"
     }
 
+ViewWindow_spec = {
+    'file': 'textview',
+    'kwds': {'title': 'Test textview',
+             'text': 'The quick brown fox jumps over the lazy dog.\n'*35,
+             '_htest': True},
+    'msg': "Test for read-only property of text.\n"
+           "Select text, scroll window, close"
+     }
+
 _widget_redirector_spec = {
     'file': 'redirector',
     'kwds': {},
index c7b49ef..f279a52 100644 (file)
@@ -6,28 +6,33 @@ Attributes and methods will be added as needed for tests.
 from idlelib.idle_test.mock_tk import Text
 
 class Func:
-    '''Mock function captures args and returns result set by test.
+    '''Record call, capture args, return/raise result set by test.
 
-    Attributes:
-    self.called - records call even if no args, kwds passed.
-    self.result - set by init, returned by call.
-    self.args - captures positional arguments.
-    self.kwds - captures keyword arguments.
+    When mock function is called, set or use attributes:
+    self.called - increment call number even if no args, kwds passed.
+    self.args - capture positional arguments.
+    self.kwds - capture keyword arguments.
+    self.result - return or raise value set in __init__.
+    self.return_self - return self instead, to mock query class return.
 
-    Most common use will probably be to mock methods.
+    Most common use will probably be to mock instance methods.
+    Given class instance, can set and delete as instance attribute.
     Mock_tk.Var and Mbox_func are special variants of this.
     '''
-    def __init__(self, result=None):
-        self.called = False
+    def __init__(self, result=None, return_self=False):
+        self.called = 0
         self.result = result
+        self.return_self = return_self
         self.args = None
         self.kwds = None
     def __call__(self, *args, **kwds):
-        self.called = True
+        self.called += 1
         self.args = args
         self.kwds = kwds
         if isinstance(self.result, BaseException):
             raise self.result
+        elif self.return_self:
+            return self
         else:
             return self.result
 
index 97e15fe..f6b7130 100644 (file)
@@ -46,6 +46,7 @@ class Get_signatureTest(unittest.TestCase):
 
         # Python class that inherits builtin methods
         class List(list): "List() doc"
+
         # Simulate builtin with no docstring for default tip test
         class SB:  __call__ = None
 
@@ -53,18 +54,29 @@ class Get_signatureTest(unittest.TestCase):
             self.assertEqual(signature(obj), out)
 
         if List.__doc__ is not None:
-            gtest(List, List.__doc__)
+            gtest(List, List.__doc__)  # This and append_doc changed in 3.7.
         gtest(list.__new__,
-               'Create and return a new object.  See help(type) for accurate signature.')
+               '(*args, **kwargs)\nCreate and return a new object.'
+               '  See help(type) for accurate signature.')
         gtest(list.__init__,
+               '(self, /, *args, **kwargs)' + ct._argument_positional + '\n' +
                'Initialize self.  See help(type(self)) for accurate signature.')
-        append_doc =  "L.append(object) -> None -- append object to end" #see3.7
+
+        append_doc =  "L.append(object) -> None -- append object to end"
         gtest(list.append, append_doc)
         gtest([].append, append_doc)
         gtest(List.append, append_doc)
 
         gtest(types.MethodType, "method(function, instance)")
         gtest(SB(), default_tip)
+        import re
+        p = re.compile('')
+        gtest(re.sub, '''(pattern, repl, string, count=0, flags=0)\nReturn the string obtained by replacing the leftmost
+non-overlapping occurrences of the pattern in string by the
+replacement repl.  repl can be either a string or a callable;
+if a string, backslash escapes in it are processed.  If it is
+a callable, it's passed the match object and must return''')
+        gtest(p.sub, '''(repl, string, count=0)\nReturn the string obtained by replacing the leftmost non-overlapping occurrences o...''')
 
     def test_signature_wrap(self):
         if textwrap.TextWrapper.__doc__ is not None:
@@ -132,12 +144,20 @@ bytes() -> empty bytes object''')
         # test that starred first parameter is *not* removed from argspec
         class C:
             def m1(*args): pass
-            def m2(**kwds): pass
         c = C()
-        for meth, mtip  in ((C.m1, '(*args)'), (c.m1, "(*args)"),
-                                      (C.m2, "(**kwds)"), (c.m2, "(**kwds)"),):
+        for meth, mtip  in ((C.m1, '(*args)'), (c.m1, "(*args)"),):
             self.assertEqual(signature(meth), mtip)
 
+    def test_invalid_method_signature(self):
+        class C:
+            def m2(**kwargs): pass
+        class Test:
+            def __call__(*, a): pass
+
+        mtip = ct._invalid_method
+        self.assertEqual(signature(C().m2), mtip)
+        self.assertEqual(signature(Test()), mtip)
+
     def test_non_ascii_name(self):
         # test that re works to delete a first parameter name that
         # includes non-ascii chars, such as various forms of A.
@@ -156,17 +176,23 @@ bytes() -> empty bytes object''')
         class NoCall:
             def __getattr__(self, name):
                 raise BaseException
-        class Call(NoCall):
+        class CallA(NoCall):
+            def __call__(oui, a, b, c):
+                pass
+        class CallB(NoCall):
             def __call__(self, ci):
                 pass
-        for meth, mtip  in ((NoCall, default_tip), (Call, default_tip),
-                            (NoCall(), ''), (Call(), '(ci)')):
+
+        for meth, mtip  in ((NoCall, default_tip), (CallA, default_tip),
+                            (NoCall(), ''), (CallA(), '(a, b, c)'),
+                            (CallB(), '(ci)')):
             self.assertEqual(signature(meth), mtip)
 
     def test_non_callables(self):
         for obj in (0, 0.0, '0', b'0', [], {}):
             self.assertEqual(signature(obj), '')
 
+
 class Get_entityTest(unittest.TestCase):
     def test_bad_entity(self):
         self.assertIsNone(ct.get_entity('1/0'))
index e678cc6..abfec79 100644 (file)
 '''Test idlelib.config.
 
-Much is tested by opening config dialog live or in test_configdialog.
-Coverage: 27%
+Coverage: 96% (100% for IdleConfParser, IdleUserConfParser*, ConfigChanges).
+* Exception is OSError clause in Save method.
+Much of IdleConf is also exercised by ConfigDialog and test_configdialog.
 '''
-from test.support import captured_stderr
+import copy
+import sys
+import os
+import tempfile
+from test.support import captured_stderr, findfile
 import unittest
+from unittest import mock
+import idlelib
 from idlelib import config
+from idlelib.idle_test.mock_idle import Func
 
 # Tests should not depend on fortuitous user configurations.
 # They must not affect actual user .cfg files.
-# Replace user parsers with empty parsers that cannot be saved.
+# Replace user parsers with empty parsers that cannot be saved
+# due to getting '' as the filename when created.
 
 idleConf = config.idleConf
 usercfg = idleConf.userCfg
 testcfg = {}
-usermain = testcfg['main'] = config.IdleUserConfParser('')  # filename
+usermain = testcfg['main'] = config.IdleUserConfParser('')
 userhigh = testcfg['highlight'] = config.IdleUserConfParser('')
 userkeys = testcfg['keys'] = config.IdleUserConfParser('')
+userextn = testcfg['extensions'] = config.IdleUserConfParser('')
 
 def setUpModule():
     idleConf.userCfg = testcfg
+    idlelib.testing = True
 
 def tearDownModule():
     idleConf.userCfg = usercfg
+    idlelib.testing = False
+
+
+class IdleConfParserTest(unittest.TestCase):
+    """Test that IdleConfParser works"""
+
+    config = """
+        [one]
+        one = false
+        two = true
+        three = 10
+
+        [two]
+        one = a string
+        two = true
+        three = false
+    """
+
+    def test_get(self):
+        parser = config.IdleConfParser('')
+        parser.read_string(self.config)
+        eq = self.assertEqual
+
+        # Test with type argument.
+        self.assertIs(parser.Get('one', 'one', type='bool'), False)
+        self.assertIs(parser.Get('one', 'two', type='bool'), True)
+        eq(parser.Get('one', 'three', type='int'), 10)
+        eq(parser.Get('two', 'one'), 'a string')
+        self.assertIs(parser.Get('two', 'two', type='bool'), True)
+        self.assertIs(parser.Get('two', 'three', type='bool'), False)
+
+        # Test without type should fallback to string.
+        eq(parser.Get('two', 'two'), 'true')
+        eq(parser.Get('two', 'three'), 'false')
+
+        # If option not exist, should return None, or default.
+        self.assertIsNone(parser.Get('not', 'exist'))
+        eq(parser.Get('not', 'exist', default='DEFAULT'), 'DEFAULT')
+
+    def test_get_option_list(self):
+        parser = config.IdleConfParser('')
+        parser.read_string(self.config)
+        get_list = parser.GetOptionList
+        self.assertCountEqual(get_list('one'), ['one', 'two', 'three'])
+        self.assertCountEqual(get_list('two'), ['one', 'two', 'three'])
+        self.assertEqual(get_list('not exist'), [])
+
+    def test_load_nothing(self):
+        parser = config.IdleConfParser('')
+        parser.Load()
+        self.assertEqual(parser.sections(), [])
+
+    def test_load_file(self):
+        # Borrow test/cfgparser.1 from test_configparser.
+        config_path = findfile('cfgparser.1')
+        parser = config.IdleConfParser(config_path)
+        parser.Load()
+
+        self.assertEqual(parser.Get('Foo Bar', 'foo'), 'newbar')
+        self.assertEqual(parser.GetOptionList('Foo Bar'), ['foo'])
+
+
+class IdleUserConfParserTest(unittest.TestCase):
+    """Test that IdleUserConfParser works"""
+
+    def new_parser(self, path=''):
+        return config.IdleUserConfParser(path)
+
+    def test_set_option(self):
+        parser = self.new_parser()
+        parser.add_section('Foo')
+        # Setting new option in existing section should return True.
+        self.assertTrue(parser.SetOption('Foo', 'bar', 'true'))
+        # Setting existing option with same value should return False.
+        self.assertFalse(parser.SetOption('Foo', 'bar', 'true'))
+        # Setting exiting option with new value should return True.
+        self.assertTrue(parser.SetOption('Foo', 'bar', 'false'))
+        self.assertEqual(parser.Get('Foo', 'bar'), 'false')
+
+        # Setting option in new section should create section and return True.
+        self.assertTrue(parser.SetOption('Bar', 'bar', 'true'))
+        self.assertCountEqual(parser.sections(), ['Bar', 'Foo'])
+        self.assertEqual(parser.Get('Bar', 'bar'), 'true')
+
+    def test_remove_option(self):
+        parser = self.new_parser()
+        parser.AddSection('Foo')
+        parser.SetOption('Foo', 'bar', 'true')
+
+        self.assertTrue(parser.RemoveOption('Foo', 'bar'))
+        self.assertFalse(parser.RemoveOption('Foo', 'bar'))
+        self.assertFalse(parser.RemoveOption('Not', 'Exist'))
+
+    def test_add_section(self):
+        parser = self.new_parser()
+        self.assertEqual(parser.sections(), [])
+
+        # Should not add duplicate section.
+        # Configparser raises DuplicateError, IdleParser not.
+        parser.AddSection('Foo')
+        parser.AddSection('Foo')
+        parser.AddSection('Bar')
+        self.assertCountEqual(parser.sections(), ['Bar', 'Foo'])
+
+    def test_remove_empty_sections(self):
+        parser = self.new_parser()
+
+        parser.AddSection('Foo')
+        parser.AddSection('Bar')
+        parser.SetOption('Idle', 'name', 'val')
+        self.assertCountEqual(parser.sections(), ['Bar', 'Foo', 'Idle'])
+        parser.RemoveEmptySections()
+        self.assertEqual(parser.sections(), ['Idle'])
+
+    def test_is_empty(self):
+        parser = self.new_parser()
+
+        parser.AddSection('Foo')
+        parser.AddSection('Bar')
+        self.assertTrue(parser.IsEmpty())
+        self.assertEqual(parser.sections(), [])
+
+        parser.SetOption('Foo', 'bar', 'false')
+        parser.AddSection('Bar')
+        self.assertFalse(parser.IsEmpty())
+        self.assertCountEqual(parser.sections(), ['Foo'])
+
+    def test_remove_file(self):
+        with tempfile.TemporaryDirectory() as tdir:
+            path = os.path.join(tdir, 'test.cfg')
+            parser = self.new_parser(path)
+            parser.RemoveFile()  # Should not raise exception.
+
+            parser.AddSection('Foo')
+            parser.SetOption('Foo', 'bar', 'true')
+            parser.Save()
+            self.assertTrue(os.path.exists(path))
+            parser.RemoveFile()
+            self.assertFalse(os.path.exists(path))
+
+    def test_save(self):
+        with tempfile.TemporaryDirectory() as tdir:
+            path = os.path.join(tdir, 'test.cfg')
+            parser = self.new_parser(path)
+            parser.AddSection('Foo')
+            parser.SetOption('Foo', 'bar', 'true')
+
+            # Should save to path when config is not empty.
+            self.assertFalse(os.path.exists(path))
+            parser.Save()
+            self.assertTrue(os.path.exists(path))
+
+            # Should remove the file from disk when config is empty.
+            parser.remove_section('Foo')
+            parser.Save()
+            self.assertFalse(os.path.exists(path))
+
+
+class IdleConfTest(unittest.TestCase):
+    """Test for idleConf"""
+
+    @classmethod
+    def setUpClass(cls):
+        cls.config_string = {}
+
+        conf = config.IdleConf(_utest=True)
+        if __name__ != '__main__':
+            idle_dir = os.path.dirname(__file__)
+        else:
+            idle_dir = os.path.abspath(sys.path[0])
+        for ctype in conf.config_types:
+            config_path = os.path.join(idle_dir, '../config-%s.def' % ctype)
+            with open(config_path, 'r') as f:
+                cls.config_string[ctype] = f.read()
+
+        cls.orig_warn = config._warn
+        config._warn = Func()
+
+    @classmethod
+    def tearDownClass(cls):
+        config._warn = cls.orig_warn
+
+    def new_config(self, _utest=False):
+        return config.IdleConf(_utest=_utest)
+
+    def mock_config(self):
+        """Return a mocked idleConf
+
+        Both default and user config used the same config-*.def
+        """
+        conf = config.IdleConf(_utest=True)
+        for ctype in conf.config_types:
+            conf.defaultCfg[ctype] = config.IdleConfParser('')
+            conf.defaultCfg[ctype].read_string(self.config_string[ctype])
+            conf.userCfg[ctype] = config.IdleUserConfParser('')
+            conf.userCfg[ctype].read_string(self.config_string[ctype])
+
+        return conf
+
+    @unittest.skipIf(sys.platform.startswith('win'), 'this is test for unix system')
+    def test_get_user_cfg_dir_unix(self):
+        "Test to get user config directory under unix"
+        conf = self.new_config(_utest=True)
+
+        # Check normal way should success
+        with mock.patch('os.path.expanduser', return_value='/home/foo'):
+            with mock.patch('os.path.exists', return_value=True):
+                self.assertEqual(conf.GetUserCfgDir(), '/home/foo/.idlerc')
+
+        # Check os.getcwd should success
+        with mock.patch('os.path.expanduser', return_value='~'):
+            with mock.patch('os.getcwd', return_value='/home/foo/cpython'):
+                with mock.patch('os.mkdir'):
+                    self.assertEqual(conf.GetUserCfgDir(),
+                                     '/home/foo/cpython/.idlerc')
+
+        # Check user dir not exists and created failed should raise SystemExit
+        with mock.patch('os.path.join', return_value='/path/not/exists'):
+            with self.assertRaises(SystemExit):
+                with self.assertRaises(FileNotFoundError):
+                    conf.GetUserCfgDir()
+
+    @unittest.skipIf(not sys.platform.startswith('win'), 'this is test for windows system')
+    def test_get_user_cfg_dir_windows(self):
+        "Test to get user config directory under windows"
+        conf = self.new_config(_utest=True)
+
+        # Check normal way should success
+        with mock.patch('os.path.expanduser', return_value='C:\\foo'):
+            with mock.patch('os.path.exists', return_value=True):
+                self.assertEqual(conf.GetUserCfgDir(), 'C:\\foo\\.idlerc')
+
+        # Check os.getcwd should success
+        with mock.patch('os.path.expanduser', return_value='~'):
+            with mock.patch('os.getcwd', return_value='C:\\foo\\cpython'):
+                with mock.patch('os.mkdir'):
+                    self.assertEqual(conf.GetUserCfgDir(),
+                                     'C:\\foo\\cpython\\.idlerc')
+
+        # Check user dir not exists and created failed should raise SystemExit
+        with mock.patch('os.path.join', return_value='/path/not/exists'):
+            with self.assertRaises(SystemExit):
+                with self.assertRaises(FileNotFoundError):
+                    conf.GetUserCfgDir()
+
+    def test_create_config_handlers(self):
+        conf = self.new_config(_utest=True)
+
+        # Mock out idle_dir
+        idle_dir = '/home/foo'
+        with mock.patch.dict({'__name__': '__foo__'}):
+            with mock.patch('os.path.dirname', return_value=idle_dir):
+                conf.CreateConfigHandlers()
+
+        # Check keys are equal
+        self.assertCountEqual(conf.defaultCfg.keys(), conf.config_types)
+        self.assertCountEqual(conf.userCfg.keys(), conf.config_types)
+
+        # Check conf parser are correct type
+        for default_parser in conf.defaultCfg.values():
+            self.assertIsInstance(default_parser, config.IdleConfParser)
+        for user_parser in conf.userCfg.values():
+            self.assertIsInstance(user_parser, config.IdleUserConfParser)
+
+        # Check config path are correct
+        for config_type, parser in conf.defaultCfg.items():
+            self.assertEqual(parser.file,
+                             os.path.join(idle_dir, 'config-%s.def' % config_type))
+        for config_type, parser in conf.userCfg.items():
+            self.assertEqual(parser.file,
+                             os.path.join(conf.userdir, 'config-%s.cfg' % config_type))
+
+    def test_load_cfg_files(self):
+        conf = self.new_config(_utest=True)
+
+        # Borrow test/cfgparser.1 from test_configparser.
+        config_path = findfile('cfgparser.1')
+        conf.defaultCfg['foo'] = config.IdleConfParser(config_path)
+        conf.userCfg['foo'] = config.IdleUserConfParser(config_path)
+
+        # Load all config from path
+        conf.LoadCfgFiles()
+
+        eq = self.assertEqual
+
+        # Check defaultCfg is loaded
+        eq(conf.defaultCfg['foo'].Get('Foo Bar', 'foo'), 'newbar')
+        eq(conf.defaultCfg['foo'].GetOptionList('Foo Bar'), ['foo'])
+
+        # Check userCfg is loaded
+        eq(conf.userCfg['foo'].Get('Foo Bar', 'foo'), 'newbar')
+        eq(conf.userCfg['foo'].GetOptionList('Foo Bar'), ['foo'])
+
+    def test_save_user_cfg_files(self):
+        conf = self.mock_config()
+
+        with mock.patch('idlelib.config.IdleUserConfParser.Save') as m:
+            conf.SaveUserCfgFiles()
+            self.assertEqual(m.call_count, len(conf.userCfg))
+
+    def test_get_option(self):
+        conf = self.mock_config()
+
+        eq = self.assertEqual
+        eq(conf.GetOption('main', 'EditorWindow', 'width'), '80')
+        eq(conf.GetOption('main', 'EditorWindow', 'width', type='int'), 80)
+        with mock.patch('idlelib.config._warn') as _warn:
+            eq(conf.GetOption('main', 'EditorWindow', 'font', type='int'), None)
+            eq(conf.GetOption('main', 'EditorWindow', 'NotExists'), None)
+            eq(conf.GetOption('main', 'EditorWindow', 'NotExists', default='NE'), 'NE')
+            eq(_warn.call_count, 4)
+
+    def test_set_option(self):
+        conf = self.mock_config()
+
+        conf.SetOption('main', 'Foo', 'bar', 'newbar')
+        self.assertEqual(conf.GetOption('main', 'Foo', 'bar'), 'newbar')
+
+    def test_get_section_list(self):
+        conf = self.mock_config()
+
+        self.assertCountEqual(
+            conf.GetSectionList('default', 'main'),
+            ['General', 'EditorWindow', 'Indent', 'Theme',
+             'Keys', 'History', 'HelpFiles'])
+        self.assertCountEqual(
+            conf.GetSectionList('user', 'main'),
+            ['General', 'EditorWindow', 'Indent', 'Theme',
+             'Keys', 'History', 'HelpFiles'])
+
+        with self.assertRaises(config.InvalidConfigSet):
+            conf.GetSectionList('foobar', 'main')
+        with self.assertRaises(config.InvalidConfigType):
+            conf.GetSectionList('default', 'notexists')
+
+    def test_get_highlight(self):
+        conf = self.mock_config()
+
+        eq = self.assertEqual
+        eq(conf.GetHighlight('IDLE Classic', 'normal'), {'foreground': '#000000',
+                                                         'background': '#ffffff'})
+        eq(conf.GetHighlight('IDLE Classic', 'normal', 'fg'), '#000000')
+        eq(conf.GetHighlight('IDLE Classic', 'normal', 'bg'), '#ffffff')
+        with self.assertRaises(config.InvalidFgBg):
+            conf.GetHighlight('IDLE Classic', 'normal', 'fb')
+
+        # Test cursor (this background should be normal-background)
+        eq(conf.GetHighlight('IDLE Classic', 'cursor'), {'foreground': 'black',
+                                                         'background': '#ffffff'})
+
+        # Test get user themes
+        conf.SetOption('highlight', 'Foobar', 'normal-foreground', '#747474')
+        conf.SetOption('highlight', 'Foobar', 'normal-background', '#171717')
+        with mock.patch('idlelib.config._warn'):
+            eq(conf.GetHighlight('Foobar', 'normal'), {'foreground': '#747474',
+                                                       'background': '#171717'})
+
+    def test_get_theme_dict(self):
+        "XXX: NOT YET DONE"
+        conf = self.mock_config()
+
+        # These two should be the same
+        self.assertEqual(
+            conf.GetThemeDict('default', 'IDLE Classic'),
+            conf.GetThemeDict('user', 'IDLE Classic'))
+
+        with self.assertRaises(config.InvalidTheme):
+            conf.GetThemeDict('bad', 'IDLE Classic')
+
+    def test_get_current_theme_and_keys(self):
+        conf = self.mock_config()
+
+        self.assertEqual(conf.CurrentTheme(), conf.current_colors_and_keys('Theme'))
+        self.assertEqual(conf.CurrentKeys(), conf.current_colors_and_keys('Keys'))
+
+    def test_current_colors_and_keys(self):
+        conf = self.mock_config()
+
+        self.assertEqual(conf.current_colors_and_keys('Theme'), 'IDLE Classic')
+
+    def test_default_keys(self):
+        current_platform = sys.platform
+        conf = self.new_config(_utest=True)
+
+        sys.platform = 'win32'
+        self.assertEqual(conf.default_keys(), 'IDLE Classic Windows')
+
+        sys.platform = 'darwin'
+        self.assertEqual(conf.default_keys(), 'IDLE Classic OSX')
+
+        sys.platform = 'some-linux'
+        self.assertEqual(conf.default_keys(), 'IDLE Modern Unix')
+
+        # Restore platform
+        sys.platform = current_platform
+
+    def test_get_extensions(self):
+        userextn.read_string('''
+            [ZzDummy]
+            enable = True
+            [DISABLE]
+            enable = False
+            ''')
+        eq = self.assertEqual
+        iGE = idleConf.GetExtensions
+        eq(iGE(shell_only=True), [])
+        eq(iGE(), ['ZzDummy'])
+        eq(iGE(editor_only=True), ['ZzDummy'])
+        eq(iGE(active_only=False), ['ZzDummy', 'DISABLE'])
+        eq(iGE(active_only=False, editor_only=True), ['ZzDummy', 'DISABLE'])
+        userextn.remove_section('ZzDummy')
+        userextn.remove_section('DISABLE')
+
+
+    def test_remove_key_bind_names(self):
+        conf = self.mock_config()
+
+        self.assertCountEqual(
+            conf.RemoveKeyBindNames(conf.GetSectionList('default', 'extensions')),
+            ['AutoComplete', 'CodeContext', 'FormatParagraph', 'ParenMatch','ZzDummy'])
+
+    def test_get_extn_name_for_event(self):
+        userextn.read_string('''
+            [ZzDummy]
+            enable = True
+            ''')
+        eq = self.assertEqual
+        eq(idleConf.GetExtnNameForEvent('z-in'), 'ZzDummy')
+        eq(idleConf.GetExtnNameForEvent('z-out'), None)
+        userextn.remove_section('ZzDummy')
+
+    def test_get_extension_keys(self):
+        userextn.read_string('''
+            [ZzDummy]
+            enable = True
+            ''')
+        self.assertEqual(idleConf.GetExtensionKeys('ZzDummy'),
+           {'<<z-in>>': ['<Control-Shift-KeyRelease-Insert>']})
+        userextn.remove_section('ZzDummy')
+# need option key test
+##        key = ['<Option-Key-2>'] if sys.platform == 'darwin' else ['<Alt-Key-2>']
+##        eq(conf.GetExtensionKeys('ZoomHeight'), {'<<zoom-height>>': key})
+
+    def test_get_extension_bindings(self):
+        userextn.read_string('''
+            [ZzDummy]
+            enable = True
+            ''')
+        eq = self.assertEqual
+        iGEB = idleConf.GetExtensionBindings
+        eq(iGEB('NotExists'), {})
+        expect = {'<<z-in>>': ['<Control-Shift-KeyRelease-Insert>'],
+                  '<<z-out>>': ['<Control-Shift-KeyRelease-Delete>']}
+        eq(iGEB('ZzDummy'), expect)
+        userextn.remove_section('ZzDummy')
+
+    def test_get_keybinding(self):
+        conf = self.mock_config()
+
+        eq = self.assertEqual
+        eq(conf.GetKeyBinding('IDLE Modern Unix', '<<copy>>'),
+            ['<Control-Shift-Key-C>', '<Control-Key-Insert>'])
+        eq(conf.GetKeyBinding('IDLE Classic Unix', '<<copy>>'),
+            ['<Alt-Key-w>', '<Meta-Key-w>'])
+        eq(conf.GetKeyBinding('IDLE Classic Windows', '<<copy>>'),
+            ['<Control-Key-c>', '<Control-Key-C>'])
+        eq(conf.GetKeyBinding('IDLE Classic Mac', '<<copy>>'), ['<Command-Key-c>'])
+        eq(conf.GetKeyBinding('IDLE Classic OSX', '<<copy>>'), ['<Command-Key-c>'])
+
+        # Test keybinding not exists
+        eq(conf.GetKeyBinding('NOT EXISTS', '<<copy>>'), [])
+        eq(conf.GetKeyBinding('IDLE Modern Unix', 'NOT EXISTS'), [])
+
+    def test_get_current_keyset(self):
+        current_platform = sys.platform
+        conf = self.mock_config()
+
+        # Ensure that platform isn't darwin
+        sys.platform = 'some-linux'
+        self.assertEqual(conf.GetCurrentKeySet(), conf.GetKeySet(conf.CurrentKeys()))
+
+        # This should not be the same, since replace <Alt- to <Option-.
+        # Above depended on config-extensions.def having Alt keys,
+        # which is no longer true.
+        # sys.platform = 'darwin'
+        # self.assertNotEqual(conf.GetCurrentKeySet(), conf.GetKeySet(conf.CurrentKeys()))
+
+        # Restore platform
+        sys.platform = current_platform
+
+    def test_get_keyset(self):
+        conf = self.mock_config()
+
+        # Conflic with key set, should be disable to ''
+        conf.defaultCfg['extensions'].add_section('Foobar')
+        conf.defaultCfg['extensions'].add_section('Foobar_cfgBindings')
+        conf.defaultCfg['extensions'].set('Foobar', 'enable', 'True')
+        conf.defaultCfg['extensions'].set('Foobar_cfgBindings', 'newfoo', '<Key-F3>')
+        self.assertEqual(conf.GetKeySet('IDLE Modern Unix')['<<newfoo>>'], '')
+
+    def test_is_core_binding(self):
+        # XXX: Should move out the core keys to config file or other place
+        conf = self.mock_config()
+
+        self.assertTrue(conf.IsCoreBinding('copy'))
+        self.assertTrue(conf.IsCoreBinding('cut'))
+        self.assertTrue(conf.IsCoreBinding('del-word-right'))
+        self.assertFalse(conf.IsCoreBinding('not-exists'))
+
+    def test_extra_help_source_list(self):
+        # Test GetExtraHelpSourceList and GetAllExtraHelpSourcesList in same
+        # place to prevent prepare input data twice.
+        conf = self.mock_config()
+
+        # Test default with no extra help source
+        self.assertEqual(conf.GetExtraHelpSourceList('default'), [])
+        self.assertEqual(conf.GetExtraHelpSourceList('user'), [])
+        with self.assertRaises(config.InvalidConfigSet):
+            self.assertEqual(conf.GetExtraHelpSourceList('bad'), [])
+        self.assertCountEqual(
+            conf.GetAllExtraHelpSourcesList(),
+            conf.GetExtraHelpSourceList('default') + conf.GetExtraHelpSourceList('user'))
+
+        # Add help source to user config
+        conf.userCfg['main'].SetOption('HelpFiles', '4', 'Python;https://python.org')  # This is bad input
+        conf.userCfg['main'].SetOption('HelpFiles', '3', 'Python:https://python.org')  # This is bad input
+        conf.userCfg['main'].SetOption('HelpFiles', '2', 'Pillow;https://pillow.readthedocs.io/en/latest/')
+        conf.userCfg['main'].SetOption('HelpFiles', '1', 'IDLE;C:/Programs/Python36/Lib/idlelib/help.html')
+        self.assertEqual(conf.GetExtraHelpSourceList('user'),
+                         [('IDLE', 'C:/Programs/Python36/Lib/idlelib/help.html', '1'),
+                          ('Pillow', 'https://pillow.readthedocs.io/en/latest/', '2'),
+                          ('Python', 'https://python.org', '4')])
+        self.assertCountEqual(
+            conf.GetAllExtraHelpSourcesList(),
+            conf.GetExtraHelpSourceList('default') + conf.GetExtraHelpSourceList('user'))
+
+    def test_get_font(self):
+        from test.support import requires
+        from tkinter import Tk
+        from tkinter.font import Font
+        conf = self.mock_config()
+
+        requires('gui')
+        root = Tk()
+        root.withdraw()
+
+        f = Font.actual(Font(name='TkFixedFont', exists=True, root=root))
+        self.assertEqual(
+            conf.GetFont(root, 'main', 'EditorWindow'),
+            (f['family'], 10 if f['size'] <= 0 else f['size'], f['weight']))
+
+        # Cleanup root
+        root.destroy()
+        del root
+
+    def test_get_core_keys(self):
+        conf = self.mock_config()
+
+        eq = self.assertEqual
+        eq(conf.GetCoreKeys()['<<center-insert>>'], ['<Control-l>'])
+        eq(conf.GetCoreKeys()['<<copy>>'], ['<Control-c>', '<Control-C>'])
+        eq(conf.GetCoreKeys()['<<history-next>>'], ['<Alt-n>'])
+        eq(conf.GetCoreKeys('IDLE Classic Windows')['<<center-insert>>'],
+           ['<Control-Key-l>', '<Control-Key-L>'])
+        eq(conf.GetCoreKeys('IDLE Classic OSX')['<<copy>>'], ['<Command-Key-c>'])
+        eq(conf.GetCoreKeys('IDLE Classic Unix')['<<history-next>>'],
+           ['<Alt-Key-n>', '<Meta-Key-n>'])
+        eq(conf.GetCoreKeys('IDLE Modern Unix')['<<history-next>>'],
+            ['<Alt-Key-n>', '<Meta-Key-n>'])
 
 
 class CurrentColorKeysTest(unittest.TestCase):
@@ -136,6 +716,92 @@ class CurrentColorKeysTest(unittest.TestCase):
         userkeys.remove_section('Custom Keys')
 
 
+class ChangesTest(unittest.TestCase):
+
+    empty = {'main':{}, 'highlight':{}, 'keys':{}, 'extensions':{}}
+
+    def load(self):  # Test_add_option verifies that this works.
+        changes = self.changes
+        changes.add_option('main', 'Msec', 'mitem', 'mval')
+        changes.add_option('highlight', 'Hsec', 'hitem', 'hval')
+        changes.add_option('keys', 'Ksec', 'kitem', 'kval')
+        return changes
+
+    loaded = {'main': {'Msec': {'mitem': 'mval'}},
+              'highlight': {'Hsec': {'hitem': 'hval'}},
+              'keys': {'Ksec': {'kitem':'kval'}},
+              'extensions': {}}
+
+    def setUp(self):
+        self.changes = config.ConfigChanges()
+
+    def test_init(self):
+        self.assertEqual(self.changes, self.empty)
+
+    def test_add_option(self):
+        changes = self.load()
+        self.assertEqual(changes, self.loaded)
+        changes.add_option('main', 'Msec', 'mitem', 'mval')
+        self.assertEqual(changes, self.loaded)
+
+    def test_save_option(self):  # Static function does not touch changes.
+        save_option = self.changes.save_option
+        self.assertTrue(save_option('main', 'Indent', 'what', '0'))
+        self.assertFalse(save_option('main', 'Indent', 'what', '0'))
+        self.assertEqual(usermain['Indent']['what'], '0')
+
+        self.assertTrue(save_option('main', 'Indent', 'use-spaces', '0'))
+        self.assertEqual(usermain['Indent']['use-spaces'], '0')
+        self.assertTrue(save_option('main', 'Indent', 'use-spaces', '1'))
+        self.assertFalse(usermain.has_option('Indent', 'use-spaces'))
+        usermain.remove_section('Indent')
+
+    def test_save_added(self):
+        changes = self.load()
+        self.assertTrue(changes.save_all())
+        self.assertEqual(usermain['Msec']['mitem'], 'mval')
+        self.assertEqual(userhigh['Hsec']['hitem'], 'hval')
+        self.assertEqual(userkeys['Ksec']['kitem'], 'kval')
+        changes.add_option('main', 'Msec', 'mitem', 'mval')
+        self.assertFalse(changes.save_all())
+        usermain.remove_section('Msec')
+        userhigh.remove_section('Hsec')
+        userkeys.remove_section('Ksec')
+
+    def test_save_help(self):
+        # Any change to HelpFiles overwrites entire section.
+        changes = self.changes
+        changes.save_option('main', 'HelpFiles', 'IDLE', 'idledoc')
+        changes.add_option('main', 'HelpFiles', 'ELDI', 'codeldi')
+        changes.save_all()
+        self.assertFalse(usermain.has_option('HelpFiles', 'IDLE'))
+        self.assertTrue(usermain.has_option('HelpFiles', 'ELDI'))
+
+    def test_save_default(self):  # Cover 2nd and 3rd false branches.
+        changes = self.changes
+        changes.add_option('main', 'Indent', 'use-spaces', '1')
+        # save_option returns False; cfg_type_changed remains False.
+
+    # TODO: test that save_all calls usercfg Saves.
+
+    def test_delete_section(self):
+        changes = self.load()
+        changes.delete_section('main', 'fake')  # Test no exception.
+        self.assertEqual(changes, self.loaded)  # Test nothing deleted.
+        for cfgtype, section in (('main', 'Msec'), ('keys', 'Ksec')):
+            testcfg[cfgtype].SetOption(section, 'name', 'value')
+            changes.delete_section(cfgtype, section)
+            with self.assertRaises(KeyError):
+                changes[cfgtype][section]  # Test section gone from changes
+                testcfg[cfgtype][section]  # and from mock userCfg.
+        # TODO test for save call.
+
+    def test_clear(self):
+        changes = self.load()
+        changes.clear()
+        self.assertEqual(changes, self.empty)
+
+
 class WarningTest(unittest.TestCase):
 
     def test_warn(self):
index 8a24c96..9074e23 100644 (file)
@@ -4,29 +4,98 @@ Coverage: 56% from creating and closing dialog.
 '''
 from idlelib import config_key
 from test.support import requires
-requires('gui')
+import sys
 import unittest
 from tkinter import Tk
+from idlelib.idle_test.mock_idle import Func
+from idlelib.idle_test.mock_tk import Var, Mbox_func
 
 
-class GetKeysTest(unittest.TestCase):
+class ValidationTest(unittest.TestCase):
+    "Test validation methods: OK, KeysOK, bind_ok."
+
+    class Validator(config_key.GetKeysDialog):
+        def __init__(self, *args, **kwargs):
+            config_key.GetKeysDialog.__init__(self, *args, **kwargs)
+            class listKeysFinal:
+                get = Func()
+            self.listKeysFinal = listKeysFinal
+        GetModifiers = Func()
+        showerror = Mbox_func()
 
     @classmethod
     def setUpClass(cls):
+        requires('gui')
         cls.root = Tk()
         cls.root.withdraw()
+        keylist = [['<Key-F12>'], ['<Control-Key-x>', '<Control-Key-X>']]
+        cls.dialog = cls.Validator(
+            cls.root, 'Title', '<<Test>>', keylist, _utest=True)
 
     @classmethod
     def tearDownClass(cls):
-        cls.root.update()  # Stop "can't run event command" warning.
+        cls.dialog.Cancel()
+        cls.root.update_idletasks()
         cls.root.destroy()
-        del cls.root
+        del cls.dialog, cls.root
+
+    def setUp(self):
+        self.dialog.showerror.message = ''
+    # A test that needs a particular final key value should set it.
+    # A test that sets a non-blank modifier list should reset it to [].
+
+    def test_ok_empty(self):
+        self.dialog.keyString.set(' ')
+        self.dialog.OK()
+        self.assertEqual(self.dialog.result, '')
+        self.assertEqual(self.dialog.showerror.message, 'No key specified.')
+
+    def test_ok_good(self):
+        self.dialog.keyString.set('<Key-F11>')
+        self.dialog.listKeysFinal.get.result = 'F11'
+        self.dialog.OK()
+        self.assertEqual(self.dialog.result, '<Key-F11>')
+        self.assertEqual(self.dialog.showerror.message, '')
+
+    def test_keys_no_ending(self):
+        self.assertFalse(self.dialog.KeysOK('<Control-Shift'))
+        self.assertIn('Missing the final', self.dialog.showerror.message)
+
+    def test_keys_no_modifier_bad(self):
+        self.dialog.listKeysFinal.get.result = 'A'
+        self.assertFalse(self.dialog.KeysOK('<Key-A>'))
+        self.assertIn('No modifier', self.dialog.showerror.message)
+
+    def test_keys_no_modifier_ok(self):
+        self.dialog.listKeysFinal.get.result = 'F11'
+        self.assertTrue(self.dialog.KeysOK('<Key-F11>'))
+        self.assertEqual(self.dialog.showerror.message, '')
+
+    def test_keys_shift_bad(self):
+        self.dialog.listKeysFinal.get.result = 'a'
+        self.dialog.GetModifiers.result = ['Shift']
+        self.assertFalse(self.dialog.KeysOK('<a>'))
+        self.assertIn('shift modifier', self.dialog.showerror.message)
+        self.dialog.GetModifiers.result = []
+
+    def test_keys_dup(self):
+        for mods, final, seq in (([], 'F12', '<Key-F12>'),
+                                 (['Control'], 'x', '<Control-Key-x>'),
+                                 (['Control'], 'X', '<Control-Key-X>')):
+            with self.subTest(m=mods, f=final, s=seq):
+                self.dialog.listKeysFinal.get.result = final
+                self.dialog.GetModifiers.result = mods
+                self.assertFalse(self.dialog.KeysOK(seq))
+                self.assertIn('already in use', self.dialog.showerror.message)
+        self.dialog.GetModifiers.result = []
 
+    def test_bind_ok(self):
+        self.assertTrue(self.dialog.bind_ok('<Control-Shift-Key-a>'))
+        self.assertEqual(self.dialog.showerror.message, '')
 
-    def test_init(self):
-        dia = config_key.GetKeysDialog(
-            self.root, 'test', '<<Test>>', ['<Key-F12>'], _utest=True)
-        dia.Cancel()
+    def test_bind_not_ok(self):
+        self.assertFalse(self.dialog.bind_ok('<Control-Shift>'))
+        self.assertIn('not accepted', self.dialog.showerror.message)
 
 
 if __name__ == '__main__':
index 3f94493..dc7f69c 100644 (file)
@@ -1,14 +1,17 @@
 """Test idlelib.configdialog.
 
 Half the class creates dialog, half works with user customizations.
-Coverage: 46% just by creating dialog, 56% with current tests.
+Coverage: 95%.
 """
-from idlelib.configdialog import ConfigDialog, idleConf  # test import
+from idlelib import configdialog
 from test.support import requires
 requires('gui')
-from tkinter import Tk
 import unittest
-import idlelib.config as config
+from unittest import mock
+from idlelib.idle_test.mock_idle import Func
+from tkinter import Tk, Frame, StringVar, IntVar, BooleanVar, DISABLED, NORMAL
+from idlelib import config
+from idlelib.configdialog import idleConf, changes, tracers
 
 # Tests should not depend on fortuitous user configurations.
 # They must not affect actual user .cfg files.
@@ -21,107 +24,1391 @@ testcfg = {
     'extensions': config.IdleUserConfParser(''),
 }
 
-# ConfigDialog.changedItems is a 3-level hierarchical dictionary of
-# pending changes that mirrors the multilevel user config dict.
-# For testing, record args in a list for comparison with expected.
-changes = []
-class TestDialog(ConfigDialog):
-    def AddChangedItem(self, *args):
-        changes.append(args)
+root = None
+dialog = None
+mainpage = changes['main']
+highpage = changes['highlight']
+keyspage = changes['keys']
+extpage = changes['extensions']
 
 def setUpModule():
-    global root, configure
+    global root, dialog
     idleConf.userCfg = testcfg
     root = Tk()
-    root.withdraw()
-    configure = TestDialog(root, 'Test', _utest=True)
-
+    # root.withdraw()    # Comment out, see issue 30870
+    dialog = configdialog.ConfigDialog(root, 'Test', _utest=True)
 
 def tearDownModule():
-    global root, configure
-    idleConf.userCfg = testcfg
-    configure.remove_var_callbacks()
-    del configure
+    global root, dialog
+    idleConf.userCfg = usercfg
+    tracers.detach()
+    tracers.clear()
+    changes.clear()
+    del dialog
     root.update_idletasks()
     root.destroy()
     del root
 
 
-class FontTabTest(unittest.TestCase):
+class FontPageTest(unittest.TestCase):
+    """Test that font widgets enable users to make font changes.
 
+    Test that widget actions set vars, that var changes add three
+    options to changes and call set_samples, and that set_samples
+    changes the font of both sample boxes.
+    """
+    @classmethod
+    def setUpClass(cls):
+        page = cls.page = dialog.fontpage
+        dialog.note.select(page)
+        page.set_samples = Func()  # Mask instance method.
+
+    @classmethod
+    def tearDownClass(cls):
+        del cls.page.set_samples  # Unmask instance method.
 
     def setUp(self):
         changes.clear()
 
-    def test_font(self):
-        # Set values guaranteed not to be defaults.
-        dfont = idleConf.GetFont(root, 'main', 'EditorWindow')
-        dsize = str(dfont[1])
-        dbold = dfont[2] == 'bold'
-        configure.fontName.set('Test Font')
-        expected = [
-            ('main', 'EditorWindow', 'font', 'Test Font'),
-            ('main', 'EditorWindow', 'font-size', dsize),
-            ('main', 'EditorWindow', 'font-bold', dbold)]
-        self.assertEqual(changes, expected)
+    def test_load_font_cfg(self):
+        # Leave widget load test to human visual check.
+        # TODO Improve checks when add IdleConf.get_font_values.
+        tracers.detach()
+        d = self.page
+        d.font_name.set('Fake')
+        d.font_size.set('1')
+        d.font_bold.set(True)
+        d.set_samples.called = 0
+        d.load_font_cfg()
+        self.assertNotEqual(d.font_name.get(), 'Fake')
+        self.assertNotEqual(d.font_size.get(), '1')
+        self.assertFalse(d.font_bold.get())
+        self.assertEqual(d.set_samples.called, 1)
+        tracers.attach()
+
+    def test_fontlist_key(self):
+        # Up and Down keys should select a new font.
+        d = self.page
+        if d.fontlist.size() < 2:
+            self.skipTest('need at least 2 fonts')
+        fontlist = d.fontlist
+        fontlist.activate(0)
+        font = d.fontlist.get('active')
+
+        # Test Down key.
+        fontlist.focus_force()
+        fontlist.update()
+        fontlist.event_generate('<Key-Down>')
+        fontlist.event_generate('<KeyRelease-Down>')
+
+        down_font = fontlist.get('active')
+        self.assertNotEqual(down_font, font)
+        self.assertIn(d.font_name.get(), down_font.lower())
+
+        # Test Up key.
+        fontlist.focus_force()
+        fontlist.update()
+        fontlist.event_generate('<Key-Up>')
+        fontlist.event_generate('<KeyRelease-Up>')
+
+        up_font = fontlist.get('active')
+        self.assertEqual(up_font, font)
+        self.assertIn(d.font_name.get(), up_font.lower())
+
+    def test_fontlist_mouse(self):
+        # Click on item should select that item.
+        d = self.page
+        if d.fontlist.size() < 2:
+            self.skipTest('need at least 2 fonts')
+        fontlist = d.fontlist
+        fontlist.activate(0)
+
+        # Select next item in listbox
+        fontlist.focus_force()
+        fontlist.see(1)
+        fontlist.update()
+        x, y, dx, dy = fontlist.bbox(1)
+        x += dx // 2
+        y += dy // 2
+        fontlist.event_generate('<Button-1>', x=x, y=y)
+        fontlist.event_generate('<ButtonRelease-1>', x=x, y=y)
+
+        font1 = fontlist.get(1)
+        select_font = fontlist.get('anchor')
+        self.assertEqual(select_font, font1)
+        self.assertIn(d.font_name.get(), font1.lower())
+
+    def test_sizelist(self):
+        # Click on number should select that number
+        d = self.page
+        d.sizelist.variable.set(40)
+        self.assertEqual(d.font_size.get(), '40')
+
+    def test_bold_toggle(self):
+        # Click on checkbutton should invert it.
+        d = self.page
+        d.font_bold.set(False)
+        d.bold_toggle.invoke()
+        self.assertTrue(d.font_bold.get())
+        d.bold_toggle.invoke()
+        self.assertFalse(d.font_bold.get())
+
+    def test_font_set(self):
+        # Test that setting a font Variable results in 3 provisional
+        # change entries and a call to set_samples. Use values sure to
+        # not be defaults.
+
+        default_font = idleConf.GetFont(root, 'main', 'EditorWindow')
+        default_size = str(default_font[1])
+        default_bold = default_font[2] == 'bold'
+        d = self.page
+        d.font_size.set(default_size)
+        d.font_bold.set(default_bold)
+        d.set_samples.called = 0
+
+        d.font_name.set('Test Font')
+        expected = {'EditorWindow': {'font': 'Test Font',
+                                     'font-size': default_size,
+                                     'font-bold': str(default_bold)}}
+        self.assertEqual(mainpage, expected)
+        self.assertEqual(d.set_samples.called, 1)
+        changes.clear()
+
+        d.font_size.set('20')
+        expected = {'EditorWindow': {'font': 'Test Font',
+                                     'font-size': '20',
+                                     'font-bold': str(default_bold)}}
+        self.assertEqual(mainpage, expected)
+        self.assertEqual(d.set_samples.called, 2)
         changes.clear()
-        configure.fontSize.set(20)
-        expected = [
-            ('main', 'EditorWindow', 'font', 'Test Font'),
-            ('main', 'EditorWindow', 'font-size', '20'),
-            ('main', 'EditorWindow', 'font-bold', dbold)]
-        self.assertEqual(changes, expected)
+
+        d.font_bold.set(not default_bold)
+        expected = {'EditorWindow': {'font': 'Test Font',
+                                     'font-size': '20',
+                                     'font-bold': str(not default_bold)}}
+        self.assertEqual(mainpage, expected)
+        self.assertEqual(d.set_samples.called, 3)
+
+    def test_set_samples(self):
+        d = self.page
+        del d.set_samples  # Unmask method for test
+        d.font_sample, d.highlight_sample = {}, {}
+        d.font_name.set('test')
+        d.font_size.set('5')
+        d.font_bold.set(1)
+        expected = {'font': ('test', '5', 'bold')}
+
+        # Test set_samples.
+        d.set_samples()
+        self.assertTrue(d.font_sample == d.highlight_sample == expected)
+
+        del d.font_sample, d.highlight_sample
+        d.set_samples = Func()  # Re-mask for other tests.
+
+
+class IndentTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.page = dialog.fontpage
+
+    def test_load_tab_cfg(self):
+        d = self.page
+        d.space_num.set(16)
+        d.load_tab_cfg()
+        self.assertEqual(d.space_num.get(), 4)
+
+    def test_indent_scale(self):
+        d = self.page
         changes.clear()
-        configure.fontBold.set(not dbold)
-        expected = [
-            ('main', 'EditorWindow', 'font', 'Test Font'),
-            ('main', 'EditorWindow', 'font-size', '20'),
-            ('main', 'EditorWindow', 'font-bold', not dbold)]
-        self.assertEqual(changes, expected)
+        d.indent_scale.set(20)
+        self.assertEqual(d.space_num.get(), 16)
+        self.assertEqual(mainpage, {'Indent': {'num-spaces': '16'}})
+
 
-    #def test_sample(self): pass  # TODO
+class HighPageTest(unittest.TestCase):
+    """Test that highlight tab widgets enable users to make changes.
 
-    def test_tabspace(self):
-        configure.spaceNum.set(6)
-        self.assertEqual(changes, [('main', 'Indent', 'num-spaces', 6)])
+    Test that widget actions set vars, that var changes add
+    options to changes and that themes work correctly.
+    """
 
+    @classmethod
+    def setUpClass(cls):
+        page = cls.page = dialog.highpage
+        dialog.note.select(page)
+        page.set_theme_type = Func()
+        page.paint_theme_sample = Func()
+        page.set_highlight_target = Func()
+        page.set_color_sample = Func()
 
-class HighlightTest(unittest.TestCase):
+    @classmethod
+    def tearDownClass(cls):
+        d = cls.page
+        del d.set_theme_type, d.paint_theme_sample
+        del d.set_highlight_target, d.set_color_sample
 
     def setUp(self):
+        d = self.page
+        # The following is needed for test_load_key_cfg, _delete_custom_keys.
+        # This may indicate a defect in some test or function.
+        for section in idleConf.GetSectionList('user', 'highlight'):
+            idleConf.userCfg['highlight'].remove_section(section)
+        changes.clear()
+        d.set_theme_type.called = 0
+        d.paint_theme_sample.called = 0
+        d.set_highlight_target.called = 0
+        d.set_color_sample.called = 0
+
+    def test_load_theme_cfg(self):
+        tracers.detach()
+        d = self.page
+        eq = self.assertEqual
+
+        # Use builtin theme with no user themes created.
+        idleConf.CurrentTheme = mock.Mock(return_value='IDLE Classic')
+        d.load_theme_cfg()
+        self.assertTrue(d.theme_source.get())
+        # builtinlist sets variable builtin_name to the CurrentTheme default.
+        eq(d.builtin_name.get(), 'IDLE Classic')
+        eq(d.custom_name.get(), '- no custom themes -')
+        eq(d.custom_theme_on.state(), ('disabled',))
+        eq(d.set_theme_type.called, 1)
+        eq(d.paint_theme_sample.called, 1)
+        eq(d.set_highlight_target.called, 1)
+
+        # Builtin theme with non-empty user theme list.
+        idleConf.SetOption('highlight', 'test1', 'option', 'value')
+        idleConf.SetOption('highlight', 'test2', 'option2', 'value2')
+        d.load_theme_cfg()
+        eq(d.builtin_name.get(), 'IDLE Classic')
+        eq(d.custom_name.get(), 'test1')
+        eq(d.set_theme_type.called, 2)
+        eq(d.paint_theme_sample.called, 2)
+        eq(d.set_highlight_target.called, 2)
+
+        # Use custom theme.
+        idleConf.CurrentTheme = mock.Mock(return_value='test2')
+        idleConf.SetOption('main', 'Theme', 'default', '0')
+        d.load_theme_cfg()
+        self.assertFalse(d.theme_source.get())
+        eq(d.builtin_name.get(), 'IDLE Classic')
+        eq(d.custom_name.get(), 'test2')
+        eq(d.set_theme_type.called, 3)
+        eq(d.paint_theme_sample.called, 3)
+        eq(d.set_highlight_target.called, 3)
+
+        del idleConf.CurrentTheme
+        tracers.attach()
+
+    def test_theme_source(self):
+        eq = self.assertEqual
+        d = self.page
+        # Test these separately.
+        d.var_changed_builtin_name = Func()
+        d.var_changed_custom_name = Func()
+        # Builtin selected.
+        d.builtin_theme_on.invoke()
+        eq(mainpage, {'Theme': {'default': 'True'}})
+        eq(d.var_changed_builtin_name.called, 1)
+        eq(d.var_changed_custom_name.called, 0)
+        changes.clear()
+
+        # Custom selected.
+        d.custom_theme_on.state(('!disabled',))
+        d.custom_theme_on.invoke()
+        self.assertEqual(mainpage, {'Theme': {'default': 'False'}})
+        eq(d.var_changed_builtin_name.called, 1)
+        eq(d.var_changed_custom_name.called, 1)
+        del d.var_changed_builtin_name, d.var_changed_custom_name
+
+    def test_builtin_name(self):
+        eq = self.assertEqual
+        d = self.page
+        item_list = ['IDLE Classic', 'IDLE Dark', 'IDLE New']
+
+        # Not in old_themes, defaults name to first item.
+        idleConf.SetOption('main', 'Theme', 'name', 'spam')
+        d.builtinlist.SetMenu(item_list, 'IDLE Dark')
+        eq(mainpage, {'Theme': {'name': 'IDLE Classic',
+                                'name2': 'IDLE Dark'}})
+        eq(d.theme_message['text'], 'New theme, see Help')
+        eq(d.paint_theme_sample.called, 1)
+
+        # Not in old themes - uses name2.
+        changes.clear()
+        idleConf.SetOption('main', 'Theme', 'name', 'IDLE New')
+        d.builtinlist.SetMenu(item_list, 'IDLE Dark')
+        eq(mainpage, {'Theme': {'name2': 'IDLE Dark'}})
+        eq(d.theme_message['text'], 'New theme, see Help')
+        eq(d.paint_theme_sample.called, 2)
+
+        # Builtin name in old_themes.
+        changes.clear()
+        d.builtinlist.SetMenu(item_list, 'IDLE Classic')
+        eq(mainpage, {'Theme': {'name': 'IDLE Classic', 'name2': ''}})
+        eq(d.theme_message['text'], '')
+        eq(d.paint_theme_sample.called, 3)
+
+    def test_custom_name(self):
+        d = self.page
+
+        # If no selections, doesn't get added.
+        d.customlist.SetMenu([], '- no custom themes -')
+        self.assertNotIn('Theme', mainpage)
+        self.assertEqual(d.paint_theme_sample.called, 0)
+
+        # Custom name selected.
         changes.clear()
+        d.customlist.SetMenu(['a', 'b', 'c'], 'c')
+        self.assertEqual(mainpage, {'Theme': {'name': 'c'}})
+        self.assertEqual(d.paint_theme_sample.called, 1)
+
+    def test_color(self):
+        d = self.page
+        d.on_new_color_set = Func()
+        # self.color is only set in get_color through ColorChooser.
+        d.color.set('green')
+        self.assertEqual(d.on_new_color_set.called, 1)
+        del d.on_new_color_set
+
+    def test_highlight_target_list_mouse(self):
+        # Set highlight_target through targetlist.
+        eq = self.assertEqual
+        d = self.page
+
+        d.targetlist.SetMenu(['a', 'b', 'c'], 'c')
+        eq(d.highlight_target.get(), 'c')
+        eq(d.set_highlight_target.called, 1)
+
+    def test_highlight_target_text_mouse(self):
+        # Set highlight_target through clicking highlight_sample.
+        eq = self.assertEqual
+        d = self.page
+
+        elem = {}
+        count = 0
+        hs = d.highlight_sample
+        hs.focus_force()
+        hs.see(1.0)
+        hs.update_idletasks()
+
+        def tag_to_element(elem):
+            for element, tag in d.theme_elements.items():
+                elem[tag[0]] = element
 
-    #def test_colorchoose(self): pass  # TODO
+        def click_it(start):
+            x, y, dx, dy = hs.bbox(start)
+            x += dx // 2
+            y += dy // 2
+            hs.event_generate('<Enter>', x=0, y=0)
+            hs.event_generate('<Motion>', x=x, y=y)
+            hs.event_generate('<ButtonPress-1>', x=x, y=y)
+            hs.event_generate('<ButtonRelease-1>', x=x, y=y)
 
+        # Flip theme_elements to make the tag the key.
+        tag_to_element(elem)
 
-class KeysTest(unittest.TestCase):
+        # If highlight_sample has a tag that isn't in theme_elements, there
+        # will be a KeyError in the test run.
+        for tag in hs.tag_names():
+            for start_index in hs.tag_ranges(tag)[0::2]:
+                count += 1
+                click_it(start_index)
+                eq(d.highlight_target.get(), elem[tag])
+                eq(d.set_highlight_target.called, count)
+
+    def test_set_theme_type(self):
+        eq = self.assertEqual
+        d = self.page
+        del d.set_theme_type
+
+        # Builtin theme selected.
+        d.theme_source.set(True)
+        d.set_theme_type()
+        eq(d.builtinlist['state'], NORMAL)
+        eq(d.customlist['state'], DISABLED)
+        eq(d.button_delete_custom.state(), ('disabled',))
+
+        # Custom theme selected.
+        d.theme_source.set(False)
+        d.set_theme_type()
+        eq(d.builtinlist['state'], DISABLED)
+        eq(d.custom_theme_on.state(), ('selected',))
+        eq(d.customlist['state'], NORMAL)
+        eq(d.button_delete_custom.state(), ())
+        d.set_theme_type = Func()
+
+    def test_get_color(self):
+        eq = self.assertEqual
+        d = self.page
+        orig_chooser = configdialog.tkColorChooser.askcolor
+        chooser = configdialog.tkColorChooser.askcolor = Func()
+        gntn = d.get_new_theme_name = Func()
+
+        d.highlight_target.set('Editor Breakpoint')
+        d.color.set('#ffffff')
+
+        # Nothing selected.
+        chooser.result = (None, None)
+        d.button_set_color.invoke()
+        eq(d.color.get(), '#ffffff')
+
+        # Selection same as previous color.
+        chooser.result = ('', d.style.lookup(d.frame_color_set['style'], 'background'))
+        d.button_set_color.invoke()
+        eq(d.color.get(), '#ffffff')
+
+        # Select different color.
+        chooser.result = ((222.8671875, 0.0, 0.0), '#de0000')
+
+        # Default theme.
+        d.color.set('#ffffff')
+        d.theme_source.set(True)
+
+        # No theme name selected therefore color not saved.
+        gntn.result = ''
+        d.button_set_color.invoke()
+        eq(gntn.called, 1)
+        eq(d.color.get(), '#ffffff')
+        # Theme name selected.
+        gntn.result = 'My New Theme'
+        d.button_set_color.invoke()
+        eq(d.custom_name.get(), gntn.result)
+        eq(d.color.get(), '#de0000')
+
+        # Custom theme.
+        d.color.set('#ffffff')
+        d.theme_source.set(False)
+        d.button_set_color.invoke()
+        eq(d.color.get(), '#de0000')
+
+        del d.get_new_theme_name
+        configdialog.tkColorChooser.askcolor = orig_chooser
+
+    def test_on_new_color_set(self):
+        d = self.page
+        color = '#3f7cae'
+        d.custom_name.set('Python')
+        d.highlight_target.set('Selected Text')
+        d.fg_bg_toggle.set(True)
+
+        d.color.set(color)
+        self.assertEqual(d.style.lookup(d.frame_color_set['style'], 'background'), color)
+        self.assertEqual(d.highlight_sample.tag_cget('hilite', 'foreground'), color)
+        self.assertEqual(highpage,
+                         {'Python': {'hilite-foreground': color}})
+
+    def test_get_new_theme_name(self):
+        orig_sectionname = configdialog.SectionName
+        sn = configdialog.SectionName = Func(return_self=True)
+        d = self.page
+
+        sn.result = 'New Theme'
+        self.assertEqual(d.get_new_theme_name(''), 'New Theme')
+
+        configdialog.SectionName = orig_sectionname
+
+    def test_save_as_new_theme(self):
+        d = self.page
+        gntn = d.get_new_theme_name = Func()
+        d.theme_source.set(True)
+
+        # No name entered.
+        gntn.result = ''
+        d.button_save_custom.invoke()
+        self.assertNotIn(gntn.result, idleConf.userCfg['highlight'])
+
+        # Name entered.
+        gntn.result = 'my new theme'
+        gntn.called = 0
+        self.assertNotIn(gntn.result, idleConf.userCfg['highlight'])
+        d.button_save_custom.invoke()
+        self.assertIn(gntn.result, idleConf.userCfg['highlight'])
+
+        del d.get_new_theme_name
+
+    def test_create_new_and_save_new(self):
+        eq = self.assertEqual
+        d = self.page
+
+        # Use default as previously active theme.
+        d.theme_source.set(True)
+        d.builtin_name.set('IDLE Classic')
+        first_new = 'my new custom theme'
+        second_new = 'my second custom theme'
+
+        # No changes, so themes are an exact copy.
+        self.assertNotIn(first_new, idleConf.userCfg)
+        d.create_new(first_new)
+        eq(idleConf.GetSectionList('user', 'highlight'), [first_new])
+        eq(idleConf.GetThemeDict('default', 'IDLE Classic'),
+           idleConf.GetThemeDict('user', first_new))
+        eq(d.custom_name.get(), first_new)
+        self.assertFalse(d.theme_source.get())  # Use custom set.
+        eq(d.set_theme_type.called, 1)
+
+        # Test that changed targets are in new theme.
+        changes.add_option('highlight', first_new, 'hit-background', 'yellow')
+        self.assertNotIn(second_new, idleConf.userCfg)
+        d.create_new(second_new)
+        eq(idleConf.GetSectionList('user', 'highlight'), [first_new, second_new])
+        self.assertNotEqual(idleConf.GetThemeDict('user', first_new),
+                            idleConf.GetThemeDict('user', second_new))
+        # Check that difference in themes was in `hit-background` from `changes`.
+        idleConf.SetOption('highlight', first_new, 'hit-background', 'yellow')
+        eq(idleConf.GetThemeDict('user', first_new),
+           idleConf.GetThemeDict('user', second_new))
+
+    def test_set_highlight_target(self):
+        eq = self.assertEqual
+        d = self.page
+        del d.set_highlight_target
+
+        # Target is cursor.
+        d.highlight_target.set('Cursor')
+        eq(d.fg_on.state(), ('disabled', 'selected'))
+        eq(d.bg_on.state(), ('disabled',))
+        self.assertTrue(d.fg_bg_toggle)
+        eq(d.set_color_sample.called, 1)
+
+        # Target is not cursor.
+        d.highlight_target.set('Comment')
+        eq(d.fg_on.state(), ('selected',))
+        eq(d.bg_on.state(), ())
+        self.assertTrue(d.fg_bg_toggle)
+        eq(d.set_color_sample.called, 2)
+
+        d.set_highlight_target = Func()
+
+    def test_set_color_sample_binding(self):
+        d = self.page
+        scs = d.set_color_sample
+
+        d.fg_on.invoke()
+        self.assertEqual(scs.called, 1)
+
+        d.bg_on.invoke()
+        self.assertEqual(scs.called, 2)
+
+    def test_set_color_sample(self):
+        d = self.page
+        del d.set_color_sample
+        d.highlight_target.set('Selected Text')
+        d.fg_bg_toggle.set(True)
+        d.set_color_sample()
+        self.assertEqual(
+                d.style.lookup(d.frame_color_set['style'], 'background'),
+                d.highlight_sample.tag_cget('hilite', 'foreground'))
+        d.set_color_sample = Func()
+
+    def test_paint_theme_sample(self):
+        eq = self.assertEqual
+        d = self.page
+        del d.paint_theme_sample
+        hs_tag = d.highlight_sample.tag_cget
+        gh = idleConf.GetHighlight
+        fg = 'foreground'
+        bg = 'background'
+
+        # Create custom theme based on IDLE Dark.
+        d.theme_source.set(True)
+        d.builtin_name.set('IDLE Dark')
+        theme = 'IDLE Test'
+        d.create_new(theme)
+        d.set_color_sample.called = 0
+
+        # Base theme with nothing in `changes`.
+        d.paint_theme_sample()
+        eq(hs_tag('break', fg), gh(theme, 'break', fgBg='fg'))
+        eq(hs_tag('cursor', bg), gh(theme, 'normal', fgBg='bg'))
+        self.assertNotEqual(hs_tag('console', fg), 'blue')
+        self.assertNotEqual(hs_tag('console', bg), 'yellow')
+        eq(d.set_color_sample.called, 1)
+
+        # Apply changes.
+        changes.add_option('highlight', theme, 'console-foreground', 'blue')
+        changes.add_option('highlight', theme, 'console-background', 'yellow')
+        d.paint_theme_sample()
+
+        eq(hs_tag('break', fg), gh(theme, 'break', fgBg='fg'))
+        eq(hs_tag('cursor', bg), gh(theme, 'normal', fgBg='bg'))
+        eq(hs_tag('console', fg), 'blue')
+        eq(hs_tag('console', bg), 'yellow')
+        eq(d.set_color_sample.called, 2)
+
+        d.paint_theme_sample = Func()
+
+    def test_delete_custom(self):
+        eq = self.assertEqual
+        d = self.page
+        d.button_delete_custom.state(('!disabled',))
+        yesno = d.askyesno = Func()
+        dialog.deactivate_current_config = Func()
+        dialog.activate_config_changes = Func()
+
+        theme_name = 'spam theme'
+        idleConf.userCfg['highlight'].SetOption(theme_name, 'name', 'value')
+        highpage[theme_name] = {'option': 'True'}
+
+        # Force custom theme.
+        d.theme_source.set(False)
+        d.custom_name.set(theme_name)
+
+        # Cancel deletion.
+        yesno.result = False
+        d.button_delete_custom.invoke()
+        eq(yesno.called, 1)
+        eq(highpage[theme_name], {'option': 'True'})
+        eq(idleConf.GetSectionList('user', 'highlight'), ['spam theme'])
+        eq(dialog.deactivate_current_config.called, 0)
+        eq(dialog.activate_config_changes.called, 0)
+        eq(d.set_theme_type.called, 0)
+
+        # Confirm deletion.
+        yesno.result = True
+        d.button_delete_custom.invoke()
+        eq(yesno.called, 2)
+        self.assertNotIn(theme_name, highpage)
+        eq(idleConf.GetSectionList('user', 'highlight'), [])
+        eq(d.custom_theme_on.state(), ('disabled',))
+        eq(d.custom_name.get(), '- no custom themes -')
+        eq(dialog.deactivate_current_config.called, 1)
+        eq(dialog.activate_config_changes.called, 1)
+        eq(d.set_theme_type.called, 1)
+
+        del dialog.activate_config_changes, dialog.deactivate_current_config
+        del d.askyesno
+
+
+class KeysPageTest(unittest.TestCase):
+    """Test that keys tab widgets enable users to make changes.
+
+    Test that widget actions set vars, that var changes add
+    options to changes and that key sets works correctly.
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        page = cls.page = dialog.keyspage
+        dialog.note.select(page)
+        page.set_keys_type = Func()
+        page.load_keys_list = Func()
+
+    @classmethod
+    def tearDownClass(cls):
+        page = cls.page
+        del page.set_keys_type, page.load_keys_list
 
     def setUp(self):
+        d = self.page
+        # The following is needed for test_load_key_cfg, _delete_custom_keys.
+        # This may indicate a defect in some test or function.
+        for section in idleConf.GetSectionList('user', 'keys'):
+            idleConf.userCfg['keys'].remove_section(section)
+        changes.clear()
+        d.set_keys_type.called = 0
+        d.load_keys_list.called = 0
+
+    def test_load_key_cfg(self):
+        tracers.detach()
+        d = self.page
+        eq = self.assertEqual
+
+        # Use builtin keyset with no user keysets created.
+        idleConf.CurrentKeys = mock.Mock(return_value='IDLE Classic OSX')
+        d.load_key_cfg()
+        self.assertTrue(d.keyset_source.get())
+        # builtinlist sets variable builtin_name to the CurrentKeys default.
+        eq(d.builtin_name.get(), 'IDLE Classic OSX')
+        eq(d.custom_name.get(), '- no custom keys -')
+        eq(d.custom_keyset_on.state(), ('disabled',))
+        eq(d.set_keys_type.called, 1)
+        eq(d.load_keys_list.called, 1)
+        eq(d.load_keys_list.args, ('IDLE Classic OSX', ))
+
+        # Builtin keyset with non-empty user keyset list.
+        idleConf.SetOption('keys', 'test1', 'option', 'value')
+        idleConf.SetOption('keys', 'test2', 'option2', 'value2')
+        d.load_key_cfg()
+        eq(d.builtin_name.get(), 'IDLE Classic OSX')
+        eq(d.custom_name.get(), 'test1')
+        eq(d.set_keys_type.called, 2)
+        eq(d.load_keys_list.called, 2)
+        eq(d.load_keys_list.args, ('IDLE Classic OSX', ))
+
+        # Use custom keyset.
+        idleConf.CurrentKeys = mock.Mock(return_value='test2')
+        idleConf.default_keys = mock.Mock(return_value='IDLE Modern Unix')
+        idleConf.SetOption('main', 'Keys', 'default', '0')
+        d.load_key_cfg()
+        self.assertFalse(d.keyset_source.get())
+        eq(d.builtin_name.get(), 'IDLE Modern Unix')
+        eq(d.custom_name.get(), 'test2')
+        eq(d.set_keys_type.called, 3)
+        eq(d.load_keys_list.called, 3)
+        eq(d.load_keys_list.args, ('test2', ))
+
+        del idleConf.CurrentKeys, idleConf.default_keys
+        tracers.attach()
+
+    def test_keyset_source(self):
+        eq = self.assertEqual
+        d = self.page
+        # Test these separately.
+        d.var_changed_builtin_name = Func()
+        d.var_changed_custom_name = Func()
+        # Builtin selected.
+        d.builtin_keyset_on.invoke()
+        eq(mainpage, {'Keys': {'default': 'True'}})
+        eq(d.var_changed_builtin_name.called, 1)
+        eq(d.var_changed_custom_name.called, 0)
+        changes.clear()
+
+        # Custom selected.
+        d.custom_keyset_on.state(('!disabled',))
+        d.custom_keyset_on.invoke()
+        self.assertEqual(mainpage, {'Keys': {'default': 'False'}})
+        eq(d.var_changed_builtin_name.called, 1)
+        eq(d.var_changed_custom_name.called, 1)
+        del d.var_changed_builtin_name, d.var_changed_custom_name
+
+    def test_builtin_name(self):
+        eq = self.assertEqual
+        d = self.page
+        idleConf.userCfg['main'].remove_section('Keys')
+        item_list = ['IDLE Classic Windows', 'IDLE Classic OSX',
+                     'IDLE Modern UNIX']
+
+        # Not in old_keys, defaults name to first item.
+        d.builtinlist.SetMenu(item_list, 'IDLE Modern UNIX')
+        eq(mainpage, {'Keys': {'name': 'IDLE Classic Windows',
+                               'name2': 'IDLE Modern UNIX'}})
+        eq(d.keys_message['text'], 'New key set, see Help')
+        eq(d.load_keys_list.called, 1)
+        eq(d.load_keys_list.args, ('IDLE Modern UNIX', ))
+
+        # Not in old keys - uses name2.
+        changes.clear()
+        idleConf.SetOption('main', 'Keys', 'name', 'IDLE Classic Unix')
+        d.builtinlist.SetMenu(item_list, 'IDLE Modern UNIX')
+        eq(mainpage, {'Keys': {'name2': 'IDLE Modern UNIX'}})
+        eq(d.keys_message['text'], 'New key set, see Help')
+        eq(d.load_keys_list.called, 2)
+        eq(d.load_keys_list.args, ('IDLE Modern UNIX', ))
+
+        # Builtin name in old_keys.
         changes.clear()
+        d.builtinlist.SetMenu(item_list, 'IDLE Classic OSX')
+        eq(mainpage, {'Keys': {'name': 'IDLE Classic OSX', 'name2': ''}})
+        eq(d.keys_message['text'], '')
+        eq(d.load_keys_list.called, 3)
+        eq(d.load_keys_list.args, ('IDLE Classic OSX', ))
+
+    def test_custom_name(self):
+        d = self.page
+
+        # If no selections, doesn't get added.
+        d.customlist.SetMenu([], '- no custom keys -')
+        self.assertNotIn('Keys', mainpage)
+        self.assertEqual(d.load_keys_list.called, 0)
+
+        # Custom name selected.
+        changes.clear()
+        d.customlist.SetMenu(['a', 'b', 'c'], 'c')
+        self.assertEqual(mainpage, {'Keys': {'name': 'c'}})
+        self.assertEqual(d.load_keys_list.called, 1)
+
+    def test_keybinding(self):
+        idleConf.SetOption('extensions', 'ZzDummy', 'enable', 'True')
+        d = self.page
+        d.custom_name.set('my custom keys')
+        d.bindingslist.delete(0, 'end')
+        d.bindingslist.insert(0, 'copy')
+        d.bindingslist.insert(1, 'z-in')
+        d.bindingslist.selection_set(0)
+        d.bindingslist.selection_anchor(0)
+        # Core binding - adds to keys.
+        d.keybinding.set('<Key-F11>')
+        self.assertEqual(keyspage,
+                         {'my custom keys': {'copy': '<Key-F11>'}})
+
+        # Not a core binding - adds to extensions.
+        d.bindingslist.selection_set(1)
+        d.bindingslist.selection_anchor(1)
+        d.keybinding.set('<Key-F11>')
+        self.assertEqual(extpage,
+                         {'ZzDummy_cfgBindings': {'z-in': '<Key-F11>'}})
+
+    def test_set_keys_type(self):
+        eq = self.assertEqual
+        d = self.page
+        del d.set_keys_type
+
+        # Builtin keyset selected.
+        d.keyset_source.set(True)
+        d.set_keys_type()
+        eq(d.builtinlist['state'], NORMAL)
+        eq(d.customlist['state'], DISABLED)
+        eq(d.button_delete_custom_keys.state(), ('disabled',))
+
+        # Custom keyset selected.
+        d.keyset_source.set(False)
+        d.set_keys_type()
+        eq(d.builtinlist['state'], DISABLED)
+        eq(d.custom_keyset_on.state(), ('selected',))
+        eq(d.customlist['state'], NORMAL)
+        eq(d.button_delete_custom_keys.state(), ())
+        d.set_keys_type = Func()
+
+    def test_get_new_keys(self):
+        eq = self.assertEqual
+        d = self.page
+        orig_getkeysdialog = configdialog.GetKeysDialog
+        gkd = configdialog.GetKeysDialog = Func(return_self=True)
+        gnkn = d.get_new_keys_name = Func()
+
+        d.button_new_keys.state(('!disabled',))
+        d.bindingslist.delete(0, 'end')
+        d.bindingslist.insert(0, 'copy - <Control-Shift-Key-C>')
+        d.bindingslist.selection_set(0)
+        d.bindingslist.selection_anchor(0)
+        d.keybinding.set('Key-a')
+        d.keyset_source.set(True)  # Default keyset.
+
+        # Default keyset; no change to binding.
+        gkd.result = ''
+        d.button_new_keys.invoke()
+        eq(d.bindingslist.get('anchor'), 'copy - <Control-Shift-Key-C>')
+        # Keybinding isn't changed when there isn't a change entered.
+        eq(d.keybinding.get(), 'Key-a')
+
+        # Default keyset; binding changed.
+        gkd.result = '<Key-F11>'
+        # No keyset name selected therefore binding not saved.
+        gnkn.result = ''
+        d.button_new_keys.invoke()
+        eq(gnkn.called, 1)
+        eq(d.bindingslist.get('anchor'), 'copy - <Control-Shift-Key-C>')
+        # Keyset name selected.
+        gnkn.result = 'My New Key Set'
+        d.button_new_keys.invoke()
+        eq(d.custom_name.get(), gnkn.result)
+        eq(d.bindingslist.get('anchor'), 'copy - <Key-F11>')
+        eq(d.keybinding.get(), '<Key-F11>')
+
+        # User keyset; binding changed.
+        d.keyset_source.set(False)  # Custom keyset.
+        gnkn.called = 0
+        gkd.result = '<Key-p>'
+        d.button_new_keys.invoke()
+        eq(gnkn.called, 0)
+        eq(d.bindingslist.get('anchor'), 'copy - <Key-p>')
+        eq(d.keybinding.get(), '<Key-p>')
+
+        del d.get_new_keys_name
+        configdialog.GetKeysDialog = orig_getkeysdialog
+
+    def test_get_new_keys_name(self):
+        orig_sectionname = configdialog.SectionName
+        sn = configdialog.SectionName = Func(return_self=True)
+        d = self.page
+
+        sn.result = 'New Keys'
+        self.assertEqual(d.get_new_keys_name(''), 'New Keys')
+
+        configdialog.SectionName = orig_sectionname
+
+    def test_save_as_new_key_set(self):
+        d = self.page
+        gnkn = d.get_new_keys_name = Func()
+        d.keyset_source.set(True)
+
+        # No name entered.
+        gnkn.result = ''
+        d.button_save_custom_keys.invoke()
+
+        # Name entered.
+        gnkn.result = 'my new key set'
+        gnkn.called = 0
+        self.assertNotIn(gnkn.result, idleConf.userCfg['keys'])
+        d.button_save_custom_keys.invoke()
+        self.assertIn(gnkn.result, idleConf.userCfg['keys'])
+
+        del d.get_new_keys_name
+
+    def test_on_bindingslist_select(self):
+        d = self.page
+        b = d.bindingslist
+        b.delete(0, 'end')
+        b.insert(0, 'copy')
+        b.insert(1, 'find')
+        b.activate(0)
+
+        b.focus_force()
+        b.see(1)
+        b.update()
+        x, y, dx, dy = b.bbox(1)
+        x += dx // 2
+        y += dy // 2
+        b.event_generate('<Enter>', x=0, y=0)
+        b.event_generate('<Motion>', x=x, y=y)
+        b.event_generate('<Button-1>', x=x, y=y)
+        b.event_generate('<ButtonRelease-1>', x=x, y=y)
+        self.assertEqual(b.get('anchor'), 'find')
+        self.assertEqual(d.button_new_keys.state(), ())
+
+    def test_create_new_key_set_and_save_new_key_set(self):
+        eq = self.assertEqual
+        d = self.page
+
+        # Use default as previously active keyset.
+        d.keyset_source.set(True)
+        d.builtin_name.set('IDLE Classic Windows')
+        first_new = 'my new custom key set'
+        second_new = 'my second custom keyset'
+
+        # No changes, so keysets are an exact copy.
+        self.assertNotIn(first_new, idleConf.userCfg)
+        d.create_new_key_set(first_new)
+        eq(idleConf.GetSectionList('user', 'keys'), [first_new])
+        eq(idleConf.GetKeySet('IDLE Classic Windows'),
+           idleConf.GetKeySet(first_new))
+        eq(d.custom_name.get(), first_new)
+        self.assertFalse(d.keyset_source.get())  # Use custom set.
+        eq(d.set_keys_type.called, 1)
+
+        # Test that changed keybindings are in new keyset.
+        changes.add_option('keys', first_new, 'copy', '<Key-F11>')
+        self.assertNotIn(second_new, idleConf.userCfg)
+        d.create_new_key_set(second_new)
+        eq(idleConf.GetSectionList('user', 'keys'), [first_new, second_new])
+        self.assertNotEqual(idleConf.GetKeySet(first_new),
+                            idleConf.GetKeySet(second_new))
+        # Check that difference in keysets was in option `copy` from `changes`.
+        idleConf.SetOption('keys', first_new, 'copy', '<Key-F11>')
+        eq(idleConf.GetKeySet(first_new), idleConf.GetKeySet(second_new))
+
+    def test_load_keys_list(self):
+        eq = self.assertEqual
+        d = self.page
+        gks = idleConf.GetKeySet = Func()
+        del d.load_keys_list
+        b = d.bindingslist
+
+        b.delete(0, 'end')
+        b.insert(0, '<<find>>')
+        b.insert(1, '<<help>>')
+        gks.result = {'<<copy>>': ['<Control-Key-c>', '<Control-Key-C>'],
+                      '<<force-open-completions>>': ['<Control-Key-space>'],
+                      '<<spam>>': ['<Key-F11>']}
+        changes.add_option('keys', 'my keys', 'spam', '<Shift-Key-a>')
+        expected = ('copy - <Control-Key-c> <Control-Key-C>',
+                    'force-open-completions - <Control-Key-space>',
+                    'spam - <Shift-Key-a>')
+
+        # No current selection.
+        d.load_keys_list('my keys')
+        eq(b.get(0, 'end'), expected)
+        eq(b.get('anchor'), '')
+        eq(b.curselection(), ())
+
+        # Check selection.
+        b.selection_set(1)
+        b.selection_anchor(1)
+        d.load_keys_list('my keys')
+        eq(b.get(0, 'end'), expected)
+        eq(b.get('anchor'), 'force-open-completions - <Control-Key-space>')
+        eq(b.curselection(), (1, ))
+
+        # Change selection.
+        b.selection_set(2)
+        b.selection_anchor(2)
+        d.load_keys_list('my keys')
+        eq(b.get(0, 'end'), expected)
+        eq(b.get('anchor'), 'spam - <Shift-Key-a>')
+        eq(b.curselection(), (2, ))
+        d.load_keys_list = Func()
+
+        del idleConf.GetKeySet
+
+    def test_delete_custom_keys(self):
+        eq = self.assertEqual
+        d = self.page
+        d.button_delete_custom_keys.state(('!disabled',))
+        yesno = d.askyesno = Func()
+        dialog.deactivate_current_config = Func()
+        dialog.activate_config_changes = Func()
+
+        keyset_name = 'spam key set'
+        idleConf.userCfg['keys'].SetOption(keyset_name, 'name', 'value')
+        keyspage[keyset_name] = {'option': 'True'}
+
+        # Force custom keyset.
+        d.keyset_source.set(False)
+        d.custom_name.set(keyset_name)
+
+        # Cancel deletion.
+        yesno.result = False
+        d.button_delete_custom_keys.invoke()
+        eq(yesno.called, 1)
+        eq(keyspage[keyset_name], {'option': 'True'})
+        eq(idleConf.GetSectionList('user', 'keys'), ['spam key set'])
+        eq(dialog.deactivate_current_config.called, 0)
+        eq(dialog.activate_config_changes.called, 0)
+        eq(d.set_keys_type.called, 0)
+
+        # Confirm deletion.
+        yesno.result = True
+        d.button_delete_custom_keys.invoke()
+        eq(yesno.called, 2)
+        self.assertNotIn(keyset_name, keyspage)
+        eq(idleConf.GetSectionList('user', 'keys'), [])
+        eq(d.custom_keyset_on.state(), ('disabled',))
+        eq(d.custom_name.get(), '- no custom keys -')
+        eq(dialog.deactivate_current_config.called, 1)
+        eq(dialog.activate_config_changes.called, 1)
+        eq(d.set_keys_type.called, 1)
 
+        del dialog.activate_config_changes, dialog.deactivate_current_config
+        del d.askyesno
 
-class GeneralTest(unittest.TestCase):
+
+class GenPageTest(unittest.TestCase):
+    """Test that general tab widgets enable users to make changes.
+
+    Test that widget actions set vars, that var changes add
+    options to changes and that helplist works correctly.
+    """
+    @classmethod
+    def setUpClass(cls):
+        page = cls.page = dialog.genpage
+        dialog.note.select(page)
+        page.set = page.set_add_delete_state = Func()
+        page.upc = page.update_help_changes = Func()
+
+    @classmethod
+    def tearDownClass(cls):
+        page = cls.page
+        del page.set, page.set_add_delete_state
+        del page.upc, page.update_help_changes
+        page.helplist.delete(0, 'end')
+        page.user_helplist.clear()
 
     def setUp(self):
         changes.clear()
 
+    def test_load_general_cfg(self):
+        # Set to wrong values, load, check right values.
+        eq = self.assertEqual
+        d = self.page
+        d.startup_edit.set(1)
+        d.autosave.set(1)
+        d.win_width.set(1)
+        d.win_height.set(1)
+        d.helplist.insert('end', 'bad')
+        d.user_helplist = ['bad', 'worse']
+        idleConf.SetOption('main', 'HelpFiles', '1', 'name;file')
+        d.load_general_cfg()
+        eq(d.startup_edit.get(), 0)
+        eq(d.autosave.get(), 0)
+        eq(d.win_width.get(), '80')
+        eq(d.win_height.get(), '40')
+        eq(d.helplist.get(0, 'end'), ('name',))
+        eq(d.user_helplist, [('name', 'file', '1')])
+
     def test_startup(self):
-        configure.radioStartupEdit.invoke()
-        self.assertEqual(changes,
-                         [('main', 'General', 'editor-on-startup', 1)])
+        d = self.page
+        d.startup_editor_on.invoke()
+        self.assertEqual(mainpage,
+                         {'General': {'editor-on-startup': '1'}})
+        changes.clear()
+        d.startup_shell_on.invoke()
+        self.assertEqual(mainpage,
+                         {'General': {'editor-on-startup': '0'}})
+
+    def test_editor_size(self):
+        d = self.page
+        d.win_height_int.delete(0, 'end')
+        d.win_height_int.insert(0, '11')
+        self.assertEqual(mainpage, {'EditorWindow': {'height': '11'}})
+        changes.clear()
+        d.win_width_int.delete(0, 'end')
+        d.win_width_int.insert(0, '11')
+        self.assertEqual(mainpage, {'EditorWindow': {'width': '11'}})
+
+    def test_autocomplete_wait(self):
+        self.page.auto_wait_int.delete(0, 'end')
+        self.page.auto_wait_int.insert(0, '11')
+        self.assertEqual(extpage, {'AutoComplete': {'popupwait': '11'}})
+
+    def test_parenmatch(self):
+        d = self.page
+        eq = self.assertEqual
+        d.paren_style_type['menu'].invoke(0)
+        eq(extpage, {'ParenMatch': {'style': 'opener'}})
+        changes.clear()
+        d.paren_flash_time.delete(0, 'end')
+        d.paren_flash_time.insert(0, '11')
+        eq(extpage, {'ParenMatch': {'flash-delay': '11'}})
+        changes.clear()
+        d.bell_on.invoke()
+        eq(extpage, {'ParenMatch': {'bell': 'False'}})
 
     def test_autosave(self):
-        configure.radioSaveAuto.invoke()
-        self.assertEqual(changes, [('main', 'General', 'autosave', 1)])
+        d = self.page
+        d.save_auto_on.invoke()
+        self.assertEqual(mainpage, {'General': {'autosave': '1'}})
+        d.save_ask_on.invoke()
+        self.assertEqual(mainpage, {'General': {'autosave': '0'}})
 
-    def test_editor_size(self):
-        configure.entryWinHeight.insert(0, '1')
-        self.assertEqual(changes, [('main', 'EditorWindow', 'height', '140')])
+    def test_paragraph(self):
+        self.page.format_width_int.delete(0, 'end')
+        self.page.format_width_int.insert(0, '11')
+        self.assertEqual(extpage, {'FormatParagraph': {'max-width': '11'}})
+
+    def test_context(self):
+        self.page.context_int.delete(0, 'end')
+        self.page.context_int.insert(0, '1')
+        self.assertEqual(extpage, {'CodeContext': {'numlines': '1'}})
+
+    def test_source_selected(self):
+        d = self.page
+        d.set = d.set_add_delete_state
+        d.upc = d.update_help_changes
+        helplist = d.helplist
+        dex = 'end'
+        helplist.insert(dex, 'source')
+        helplist.activate(dex)
+
+        helplist.focus_force()
+        helplist.see(dex)
+        helplist.update()
+        x, y, dx, dy = helplist.bbox(dex)
+        x += dx // 2
+        y += dy // 2
+        d.set.called = d.upc.called = 0
+        helplist.event_generate('<Enter>', x=0, y=0)
+        helplist.event_generate('<Motion>', x=x, y=y)
+        helplist.event_generate('<Button-1>', x=x, y=y)
+        helplist.event_generate('<ButtonRelease-1>', x=x, y=y)
+        self.assertEqual(helplist.get('anchor'), 'source')
+        self.assertTrue(d.set.called)
+        self.assertFalse(d.upc.called)
+
+    def test_set_add_delete_state(self):
+        # Call with 0 items, 1 unselected item, 1 selected item.
+        eq = self.assertEqual
+        d = self.page
+        del d.set_add_delete_state  # Unmask method.
+        sad = d.set_add_delete_state
+        h = d.helplist
+
+        h.delete(0, 'end')
+        sad()
+        eq(d.button_helplist_edit.state(), ('disabled',))
+        eq(d.button_helplist_remove.state(), ('disabled',))
+
+        h.insert(0, 'source')
+        sad()
+        eq(d.button_helplist_edit.state(), ('disabled',))
+        eq(d.button_helplist_remove.state(), ('disabled',))
+
+        h.selection_set(0)
+        sad()
+        eq(d.button_helplist_edit.state(), ())
+        eq(d.button_helplist_remove.state(), ())
+        d.set_add_delete_state = Func()  # Mask method.
+
+    def test_helplist_item_add(self):
+        # Call without and twice with HelpSource result.
+        # Double call enables check on order.
+        eq = self.assertEqual
+        orig_helpsource = configdialog.HelpSource
+        hs = configdialog.HelpSource = Func(return_self=True)
+        d = self.page
+        d.helplist.delete(0, 'end')
+        d.user_helplist.clear()
+        d.set.called = d.upc.called = 0
+
+        hs.result = ''
+        d.helplist_item_add()
+        self.assertTrue(list(d.helplist.get(0, 'end')) ==
+                        d.user_helplist == [])
+        self.assertFalse(d.upc.called)
+
+        hs.result = ('name1', 'file1')
+        d.helplist_item_add()
+        hs.result = ('name2', 'file2')
+        d.helplist_item_add()
+        eq(d.helplist.get(0, 'end'), ('name1', 'name2'))
+        eq(d.user_helplist, [('name1', 'file1'), ('name2', 'file2')])
+        eq(d.upc.called, 2)
+        self.assertFalse(d.set.called)
+
+        configdialog.HelpSource = orig_helpsource
+
+    def test_helplist_item_edit(self):
+        # Call without and with HelpSource change.
+        eq = self.assertEqual
+        orig_helpsource = configdialog.HelpSource
+        hs = configdialog.HelpSource = Func(return_self=True)
+        d = self.page
+        d.helplist.delete(0, 'end')
+        d.helplist.insert(0, 'name1')
+        d.helplist.selection_set(0)
+        d.helplist.selection_anchor(0)
+        d.user_helplist.clear()
+        d.user_helplist.append(('name1', 'file1'))
+        d.set.called = d.upc.called = 0
+
+        hs.result = ''
+        d.helplist_item_edit()
+        hs.result = ('name1', 'file1')
+        d.helplist_item_edit()
+        eq(d.helplist.get(0, 'end'), ('name1',))
+        eq(d.user_helplist, [('name1', 'file1')])
+        self.assertFalse(d.upc.called)
+
+        hs.result = ('name2', 'file2')
+        d.helplist_item_edit()
+        eq(d.helplist.get(0, 'end'), ('name2',))
+        eq(d.user_helplist, [('name2', 'file2')])
+        self.assertTrue(d.upc.called == d.set.called == 1)
+
+        configdialog.HelpSource = orig_helpsource
+
+    def test_helplist_item_remove(self):
+        eq = self.assertEqual
+        d = self.page
+        d.helplist.delete(0, 'end')
+        d.helplist.insert(0, 'name1')
+        d.helplist.selection_set(0)
+        d.helplist.selection_anchor(0)
+        d.user_helplist.clear()
+        d.user_helplist.append(('name1', 'file1'))
+        d.set.called = d.upc.called = 0
+
+        d.helplist_item_remove()
+        eq(d.helplist.get(0, 'end'), ())
+        eq(d.user_helplist, [])
+        self.assertTrue(d.upc.called == d.set.called == 1)
+
+    def test_update_help_changes(self):
+        d = self.page
+        del d.update_help_changes
+        d.user_helplist.clear()
+        d.user_helplist.append(('name1', 'file1'))
+        d.user_helplist.append(('name2', 'file2'))
+
+        d.update_help_changes()
+        self.assertEqual(mainpage['HelpFiles'],
+                         {'1': 'name1;file1', '2': 'name2;file2'})
+        d.update_help_changes = Func()
+
+
+class VarTraceTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.tracers = configdialog.VarTrace()
+        cls.iv = IntVar(root)
+        cls.bv = BooleanVar(root)
+
+    @classmethod
+    def tearDownClass(cls):
+        del cls.tracers, cls.iv, cls.bv
+
+    def setUp(self):
+        self.tracers.clear()
+        self.called = 0
+
+    def var_changed_increment(self, *params):
+        self.called += 13
+
+    def var_changed_boolean(self, *params):
+        pass
+
+    def test_init(self):
+        tr = self.tracers
+        tr.__init__()
+        self.assertEqual(tr.untraced, [])
+        self.assertEqual(tr.traced, [])
+
+    def test_clear(self):
+        tr = self.tracers
+        tr.untraced.append(0)
+        tr.traced.append(1)
+        tr.clear()
+        self.assertEqual(tr.untraced, [])
+        self.assertEqual(tr.traced, [])
+
+    def test_add(self):
+        tr = self.tracers
+        func = Func()
+        cb = tr.make_callback = mock.Mock(return_value=func)
+
+        iv = tr.add(self.iv, self.var_changed_increment)
+        self.assertIs(iv, self.iv)
+        bv = tr.add(self.bv, self.var_changed_boolean)
+        self.assertIs(bv, self.bv)
+
+        sv = StringVar(root)
+        sv2 = tr.add(sv, ('main', 'section', 'option'))
+        self.assertIs(sv2, sv)
+        cb.assert_called_once()
+        cb.assert_called_with(sv, ('main', 'section', 'option'))
+
+        expected = [(iv, self.var_changed_increment),
+                    (bv, self.var_changed_boolean),
+                    (sv, func)]
+        self.assertEqual(tr.traced, [])
+        self.assertEqual(tr.untraced, expected)
+
+        del tr.make_callback
+
+    def test_make_callback(self):
+        cb = self.tracers.make_callback(self.iv, ('main', 'section', 'option'))
+        self.assertTrue(callable(cb))
+        self.iv.set(42)
+        # Not attached, so set didn't invoke the callback.
+        self.assertNotIn('section', changes['main'])
+        # Invoke callback manually.
+        cb()
+        self.assertIn('section', changes['main'])
+        self.assertEqual(changes['main']['section']['option'], '42')
         changes.clear()
-        configure.entryWinWidth.insert(0, '1')
-        self.assertEqual(changes, [('main', 'EditorWindow', 'width', '180')])
 
-    #def test_help_sources(self): pass  # TODO
+    def test_attach_detach(self):
+        tr = self.tracers
+        iv = tr.add(self.iv, self.var_changed_increment)
+        bv = tr.add(self.bv, self.var_changed_boolean)
+        expected = [(iv, self.var_changed_increment),
+                    (bv, self.var_changed_boolean)]
+
+        # Attach callbacks and test call increment.
+        tr.attach()
+        self.assertEqual(tr.untraced, [])
+        self.assertCountEqual(tr.traced, expected)
+        iv.set(1)
+        self.assertEqual(iv.get(), 1)
+        self.assertEqual(self.called, 13)
+
+        # Check that only one callback is attached to a variable.
+        # If more than one callback were attached, then var_changed_increment
+        # would be called twice and the counter would be 2.
+        self.called = 0
+        tr.attach()
+        iv.set(1)
+        self.assertEqual(self.called, 13)
+
+        # Detach callbacks.
+        self.called = 0
+        tr.detach()
+        self.assertEqual(tr.traced, [])
+        self.assertCountEqual(tr.untraced, expected)
+        iv.set(1)
+        self.assertEqual(self.called, 0)
 
 
 if __name__ == '__main__':
index 15d1b6b..1f67aad 100644 (file)
@@ -5,10 +5,15 @@ Coverage: 100%
 from test.support import requires, findfile
 from tkinter import Tk, TclError
 import unittest
+from unittest import mock
 from idlelib.idle_test.mock_idle import Func
 from idlelib.idle_test.mock_tk import Mbox_func
 from idlelib.help_about import AboutDialog as About
+from idlelib import help_about
 from idlelib import textview
+import os.path
+from platform import python_version, architecture
+
 
 class LiveDialogTest(unittest.TestCase):
     """Simulate user clicking buttons other than [Close].
@@ -29,26 +34,33 @@ class LiveDialogTest(unittest.TestCase):
         cls.root.destroy()
         del cls.root
 
+    def test_build_bits(self):
+        self.assertIn(help_about.build_bits(), ('32', '64'))
+
     def test_dialog_title(self):
         """Test about dialog title"""
         self.assertEqual(self.dialog.title(), 'About IDLE')
 
+    def test_dialog_logo(self):
+        """Test about dialog logo."""
+        path, file = os.path.split(self.dialog.icon_image['file'])
+        fn, ext = os.path.splitext(file)
+        self.assertEqual(fn, 'idle_48')
+
     def test_printer_buttons(self):
         """Test buttons whose commands use printer function."""
         dialog = self.dialog
-        button_sources = [(self.dialog.py_license, license),
-                          (self.dialog.py_copyright, copyright),
-                          (self.dialog.py_credits, credits)]
+        button_sources = [(dialog.py_license, license),
+                          (dialog.py_copyright, copyright),
+                          (dialog.py_credits, credits)]
 
         for button, printer in button_sources:
             printer._Printer__setup()
             button.invoke()
+            get = dialog._current_textview.viewframe.textframe.text.get
+            self.assertEqual(printer._Printer__lines[0], get('1.0', '1.end'))
             self.assertEqual(
-                    printer._Printer__lines[0],
-                    dialog._current_textview.text.get('1.0', '1.end'))
-            self.assertEqual(
-                    printer._Printer__lines[1],
-                    dialog._current_textview.text.get('2.0', '2.end'))
+                    printer._Printer__lines[1], get('2.0', '2.end'))
             dialog._current_textview.destroy()
 
     def test_file_buttons(self):
@@ -61,17 +73,38 @@ class LiveDialogTest(unittest.TestCase):
         for button, filename in button_sources:
             button.invoke()
             fn = findfile(filename, subdir='idlelib')
+            get = dialog._current_textview.viewframe.textframe.text.get
             with open(fn) as f:
-                self.assertEqual(
-                        f.readline().strip(),
-                        dialog._current_textview.text.get('1.0', '1.end'))
+                self.assertEqual(f.readline().strip(), get('1.0', '1.end'))
                 f.readline()
-                self.assertEqual(
-                    f.readline().strip(),
-                    dialog._current_textview.text.get('3.0', '3.end'))
+                self.assertEqual(f.readline().strip(), get('3.0', '3.end'))
             dialog._current_textview.destroy()
 
 
+class DefaultTitleTest(unittest.TestCase):
+    "Test default title."
+
+    @classmethod
+    def setUpClass(cls):
+        requires('gui')
+        cls.root = Tk()
+        cls.root.withdraw()
+        cls.dialog = About(cls.root, _utest=True)
+
+    @classmethod
+    def tearDownClass(cls):
+        del cls.dialog
+        cls.root.update_idletasks()
+        cls.root.destroy()
+        del cls.root
+
+    def test_dialog_title(self):
+        """Test about dialog title"""
+        self.assertEqual(self.dialog.title(),
+                         f'About IDLE {python_version()}'
+                         f' ({help_about.build_bits()} bit)')
+
+
 class CloseTest(unittest.TestCase):
     """Simulate user clicking [Close] button"""
 
diff --git a/Lib/idlelib/idle_test/test_outwin.py b/Lib/idlelib/idle_test/test_outwin.py
new file mode 100644 (file)
index 0000000..231c7bf
--- /dev/null
@@ -0,0 +1,172 @@
+""" Test idlelib.outwin.
+"""
+
+import unittest
+from tkinter import Tk, Text
+from idlelib.idle_test.mock_tk import Mbox_func
+from idlelib.idle_test.mock_idle import Func
+from idlelib import outwin
+from test.support import requires
+from unittest import mock
+
+
+class OutputWindowTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        requires('gui')
+        root = cls.root = Tk()
+        root.withdraw()
+        w = cls.window = outwin.OutputWindow(None, None, None, root)
+        cls.text = w.text = Text(root)
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.window.close()
+        del cls.text, cls.window
+        cls.root.destroy()
+        del cls.root
+
+    def setUp(self):
+        self.text.delete('1.0', 'end')
+
+    def test_ispythonsource(self):
+        # OutputWindow overrides ispythonsource to always return False.
+        w = self.window
+        self.assertFalse(w.ispythonsource('test.txt'))
+        self.assertFalse(w.ispythonsource(__file__))
+
+    def test_window_title(self):
+        self.assertEqual(self.window.top.title(), 'Output')
+
+    def test_maybesave(self):
+        w = self.window
+        eq = self.assertEqual
+        w.get_saved = Func()
+
+        w.get_saved.result = False
+        eq(w.maybesave(), 'no')
+        eq(w.get_saved.called, 1)
+
+        w.get_saved.result = True
+        eq(w.maybesave(), 'yes')
+        eq(w.get_saved.called, 2)
+        del w.get_saved
+
+    def test_write(self):
+        eq = self.assertEqual
+        delete = self.text.delete
+        get = self.text.get
+        write = self.window.write
+
+        # Test bytes.
+        b = b'Test bytes.'
+        eq(write(b), len(b))
+        eq(get('1.0', '1.end'), b.decode())
+
+        # No new line - insert stays on same line.
+        delete('1.0', 'end')
+        test_text = 'test text'
+        eq(write(test_text), len(test_text))
+        eq(get('1.0', '1.end'), 'test text')
+        eq(get('insert linestart', 'insert lineend'), 'test text')
+
+        # New line - insert moves to next line.
+        delete('1.0', 'end')
+        test_text = 'test text\n'
+        eq(write(test_text), len(test_text))
+        eq(get('1.0', '1.end'), 'test text')
+        eq(get('insert linestart', 'insert lineend'), '')
+
+        # Text after new line is tagged for second line of Text widget.
+        delete('1.0', 'end')
+        test_text = 'test text\nLine 2'
+        eq(write(test_text), len(test_text))
+        eq(get('1.0', '1.end'), 'test text')
+        eq(get('2.0', '2.end'), 'Line 2')
+        eq(get('insert linestart', 'insert lineend'), 'Line 2')
+
+        # Test tags.
+        delete('1.0', 'end')
+        test_text = 'test text\n'
+        test_text2 = 'Line 2\n'
+        eq(write(test_text, tags='mytag'), len(test_text))
+        eq(write(test_text2, tags='secondtag'), len(test_text2))
+        eq(get('mytag.first', 'mytag.last'), test_text)
+        eq(get('secondtag.first', 'secondtag.last'), test_text2)
+        eq(get('1.0', '1.end'), test_text.rstrip('\n'))
+        eq(get('2.0', '2.end'), test_text2.rstrip('\n'))
+
+    def test_writelines(self):
+        eq = self.assertEqual
+        get = self.text.get
+        writelines = self.window.writelines
+
+        writelines(('Line 1\n', 'Line 2\n', 'Line 3\n'))
+        eq(get('1.0', '1.end'), 'Line 1')
+        eq(get('2.0', '2.end'), 'Line 2')
+        eq(get('3.0', '3.end'), 'Line 3')
+        eq(get('insert linestart', 'insert lineend'), '')
+
+    def test_goto_file_line(self):
+        eq = self.assertEqual
+        w = self.window
+        text = self.text
+
+        w.flist = mock.Mock()
+        gfl = w.flist.gotofileline = Func()
+        showerror = w.showerror = Mbox_func()
+
+        # No file/line number.
+        w.write('Not a file line')
+        self.assertIsNone(w.goto_file_line())
+        eq(gfl.called, 0)
+        eq(showerror.title, 'No special line')
+
+        # Current file/line number.
+        w.write(f'{str(__file__)}: 42: spam\n')
+        w.write(f'{str(__file__)}: 21: spam')
+        self.assertIsNone(w.goto_file_line())
+        eq(gfl.args, (str(__file__), 21))
+
+        # Previous line has file/line number.
+        text.delete('1.0', 'end')
+        w.write(f'{str(__file__)}: 42: spam\n')
+        w.write('Not a file line')
+        self.assertIsNone(w.goto_file_line())
+        eq(gfl.args, (str(__file__), 42))
+
+        del w.flist.gotofileline, w.showerror
+
+
+class ModuleFunctionTest(unittest.TestCase):
+
+    @classmethod
+    def setUp(cls):
+        outwin.file_line_progs = None
+
+    def test_compile_progs(self):
+        outwin.compile_progs()
+        for pat, regex in zip(outwin.file_line_pats, outwin.file_line_progs):
+            self.assertEqual(regex.pattern, pat)
+
+    @mock.patch('builtins.open')
+    def test_file_line_helper(self, mock_open):
+        flh = outwin.file_line_helper
+        test_lines = (
+            (r'foo file "testfile1", line 42, bar', ('testfile1', 42)),
+            (r'foo testfile2(21) bar', ('testfile2', 21)),
+            (r'  testfile3  : 42: foo bar\n', ('  testfile3  ', 42)),
+            (r'foo testfile4.py :1: ', ('foo testfile4.py ', 1)),
+            ('testfile5: \u19D4\u19D2: ', ('testfile5', 42)),
+            (r'testfile6: 42', None),       # only one `:`
+            (r'testfile7 42 text', None)    # no separators
+            )
+        for line, expected_output in test_lines:
+            self.assertEqual(flh(line), expected_output)
+            if expected_output:
+                mock_open.assert_called_with(expected_output[0], 'r')
+
+
+if __name__ == '__main__':
+    unittest.main(verbosity=2)
index b5fd0d1..3caa275 100644 (file)
@@ -3,13 +3,14 @@
 This must currently be a gui test because ParenMatch methods use
 several text methods not defined on idlelib.idle_test.mock_tk.Text.
 '''
+from idlelib.parenmatch import ParenMatch
 from test.support import requires
 requires('gui')
 
 import unittest
 from unittest.mock import Mock
 from tkinter import Tk, Text
-from idlelib.parenmatch import ParenMatch
+
 
 class DummyEditwin:
     def __init__(self, text):
@@ -44,46 +45,39 @@ class ParenMatchTest(unittest.TestCase):
         pm.bell = lambda: None
         return pm
 
-    def test_paren_expression(self):
+    def test_paren_styles(self):
         """
-        Test ParenMatch with 'expression' style.
+        Test ParenMatch with each style.
         """
         text = self.text
         pm = self.get_parenmatch()
-        pm.set_style('expression')
-
-        text.insert('insert', 'def foobar(a, b')
-        pm.flash_paren_event('event')
-        self.assertIn('<<parenmatch-check-restore>>', text.event_info())
-        self.assertTupleEqual(text.tag_prevrange('paren', 'end'),
-                             ('1.10', '1.15'))
-        text.insert('insert', ')')
-        pm.restore_event()
-        self.assertNotIn('<<parenmatch-check-restore>>', text.event_info())
-        self.assertEqual(text.tag_prevrange('paren', 'end'), ())
-
-        # paren_closed_event can only be tested as below
-        pm.paren_closed_event('event')
-        self.assertTupleEqual(text.tag_prevrange('paren', 'end'),
-                                                ('1.10', '1.16'))
-
-    def test_paren_default(self):
-        """
-        Test ParenMatch with 'default' style.
-        """
-        text = self.text
-        pm = self.get_parenmatch()
-        pm.set_style('default')
-
-        text.insert('insert', 'def foobar(a, b')
-        pm.flash_paren_event('event')
-        self.assertIn('<<parenmatch-check-restore>>', text.event_info())
-        self.assertTupleEqual(text.tag_prevrange('paren', 'end'),
-                             ('1.10', '1.11'))
-        text.insert('insert', ')')
-        pm.restore_event()
-        self.assertNotIn('<<parenmatch-check-restore>>', text.event_info())
-        self.assertEqual(text.tag_prevrange('paren', 'end'), ())
+        for style, range1, range2 in (
+                ('opener', ('1.10', '1.11'), ('1.10', '1.11')),
+                ('default',('1.10', '1.11'),('1.10', '1.11')),
+                ('parens', ('1.14', '1.15'), ('1.15', '1.16')),
+                ('expression', ('1.10', '1.15'), ('1.10', '1.16'))):
+            with self.subTest(style=style):
+                text.delete('1.0', 'end')
+                pm.STYLE = style
+                text.insert('insert', 'def foobar(a, b')
+
+                pm.flash_paren_event('event')
+                self.assertIn('<<parenmatch-check-restore>>', text.event_info())
+                if style == 'parens':
+                    self.assertTupleEqual(text.tag_nextrange('paren', '1.0'),
+                                          ('1.10', '1.11'))
+                self.assertTupleEqual(
+                        text.tag_prevrange('paren', 'end'), range1)
+
+                text.insert('insert', ')')
+                pm.restore_event()
+                self.assertNotIn('<<parenmatch-check-restore>>',
+                                 text.event_info())
+                self.assertEqual(text.tag_prevrange('paren', 'end'), ())
+
+                pm.paren_closed_event('event')
+                self.assertTupleEqual(
+                        text.tag_prevrange('paren', 'end'), range2)
 
     def test_paren_corner(self):
         """
@@ -95,14 +89,14 @@ class ParenMatchTest(unittest.TestCase):
         pm = self.get_parenmatch()
 
         text.insert('insert', '# this is a commen)')
-        self.assertIsNone(pm.paren_closed_event('event'))
+        pm.paren_closed_event('event')
 
         text.insert('insert', '\ndef')
-        self.assertIsNone(pm.flash_paren_event('event'))
-        self.assertIsNone(pm.paren_closed_event('event'))
+        pm.flash_paren_event('event')
+        pm.paren_closed_event('event')
 
         text.insert('insert', ' a, *arg)')
-        self.assertIsNone(pm.paren_closed_event('event'))
+        pm.paren_closed_event('event')
 
     def test_handle_restore_timer(self):
         pm = self.get_parenmatch()
index 7a2f7e4..c129c2f 100644 (file)
@@ -1,7 +1,7 @@
 '''Test idlelib.textview.
 
-Since all methods and functions create (or destroy) a TextViewer, which
-is a widget containing multiple widgets, all tests must be gui tests.
+Since all methods and functions create (or destroy) a ViewWindow, which
+is a widget containing a widget, etcetera, all tests must be gui tests.
 Using mock Text would not change this.  Other mocks are used to retrieve
 information about calls.
 
@@ -13,7 +13,8 @@ requires('gui')
 
 import unittest
 import os
-from tkinter import Tk, Button
+from tkinter import Tk
+from tkinter.ttk import Button
 from idlelib.idle_test.mock_idle import Func
 from idlelib.idle_test.mock_tk import Mbox_func
 
@@ -25,44 +26,44 @@ def setUpModule():
 def tearDownModule():
     global root
     root.update_idletasks()
-    root.destroy()  # Pyflakes falsely sees root as undefined.
+    root.destroy()
     del root
 
-# If we call TextViewer or wrapper functions with defaults
+# If we call ViewWindow or wrapper functions with defaults
 # modal=True, _utest=False, test hangs on call to wait_window.
 # Have also gotten tk error 'can't invoke "event" command'.
 
 
-class TV(tv.TextViewer):  # Used in TextViewTest.
+class VW(tv.ViewWindow):  # Used in ViewWindowTest.
     transient = Func()
     grab_set = Func()
     wait_window = Func()
 
 
-# Call wrapper class with mock wait_window.
-class TextViewTest(unittest.TestCase):
+# Call wrapper class VW with mock wait_window.
+class ViewWindowTest(unittest.TestCase):
 
     def setUp(self):
-        TV.transient.__init__()
-        TV.grab_set.__init__()
-        TV.wait_window.__init__()
+        VW.transient.__init__()
+        VW.grab_set.__init__()
+        VW.wait_window.__init__()
 
     def test_init_modal(self):
-        view = TV(root, 'Title', 'test text')
-        self.assertTrue(TV.transient.called)
-        self.assertTrue(TV.grab_set.called)
-        self.assertTrue(TV.wait_window.called)
+        view = VW(root, 'Title', 'test text')
+        self.assertTrue(VW.transient.called)
+        self.assertTrue(VW.grab_set.called)
+        self.assertTrue(VW.wait_window.called)
         view.ok()
 
     def test_init_nonmodal(self):
-        view = TV(root, 'Title', 'test text', modal=False)
-        self.assertFalse(TV.transient.called)
-        self.assertFalse(TV.grab_set.called)
-        self.assertFalse(TV.wait_window.called)
+        view = VW(root, 'Title', 'test text', modal=False)
+        self.assertFalse(VW.transient.called)
+        self.assertFalse(VW.grab_set.called)
+        self.assertFalse(VW.wait_window.called)
         view.ok()
 
     def test_ok(self):
-        view = TV(root, 'Title', 'test text', modal=False)
+        view = VW(root, 'Title', 'test text', modal=False)
         view.destroy = Func()
         view.ok()
         self.assertTrue(view.destroy.called)
@@ -70,7 +71,28 @@ class TextViewTest(unittest.TestCase):
         view.destroy()
 
 
-# Call TextViewer with modal=False.
+class TextFrameTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        "By itself, this tests that file parsed without exception."
+        cls.root = root = Tk()
+        root.withdraw()
+        cls.frame = tv.TextFrame(root, 'test text')
+
+    @classmethod
+    def tearDownClass(cls):
+        del cls.frame
+        cls.root.update_idletasks()
+        cls.root.destroy()
+        del cls.root
+
+    def test_line1(self):
+        get = self.frame.text.get
+        self.assertEqual(get('1.0', '1.end'), 'test text')
+
+
+# Call ViewWindow with modal=False.
 class ViewFunctionTest(unittest.TestCase):
 
     @classmethod
@@ -85,13 +107,16 @@ class ViewFunctionTest(unittest.TestCase):
 
     def test_view_text(self):
         view = tv.view_text(root, 'Title', 'test text', modal=False)
-        self.assertIsInstance(view, tv.TextViewer)
+        self.assertIsInstance(view, tv.ViewWindow)
+        self.assertIsInstance(view.viewframe, tv.ViewFrame)
         view.ok()
 
     def test_view_file(self):
         view = tv.view_file(root, 'Title', __file__, modal=False)
-        self.assertIsInstance(view, tv.TextViewer)
-        self.assertIn('Test', view.text.get('1.0', '1.end'))
+        self.assertIsInstance(view, tv.ViewWindow)
+        self.assertIsInstance(view.viewframe, tv.ViewFrame)
+        get = view.viewframe.textframe.text.get
+        self.assertIn('Test', get('1.0', '1.end'))
         view.ok()
 
     def test_bad_file(self):
@@ -109,8 +134,7 @@ class ViewFunctionTest(unittest.TestCase):
         self.assertEqual(tv.showerror.title, 'Unicode Decode Error')
 
 
-
-# Call TextViewer with _utest=True.
+# Call ViewWindow with _utest=True.
 class ButtonClickTest(unittest.TestCase):
 
     def setUp(self):
@@ -131,7 +155,8 @@ class ButtonClickTest(unittest.TestCase):
 
         self.assertEqual(self.called, True)
         self.assertEqual(self.view.title(), 'TITLE_TEXT')
-        self.assertEqual(self.view.text.get('1.0', '1.end'), 'COMMAND')
+        self.assertEqual(self.view.viewframe.textframe.text.get('1.0', '1.end'),
+                         'COMMAND')
 
     def test_view_file_bind_with_button(self):
         def _command():
@@ -143,12 +168,11 @@ class ButtonClickTest(unittest.TestCase):
 
         self.assertEqual(self.called, True)
         self.assertEqual(self.view.title(), 'TITLE_FILE')
+        get = self.view.viewframe.textframe.text.get
         with open(__file__) as f:
-            self.assertEqual(self.view.text.get('1.0', '1.end'),
-                             f.readline().strip())
+            self.assertEqual(get('1.0', '1.end'), f.readline().strip())
             f.readline()
-            self.assertEqual(self.view.text.get('3.0', '3.end'),
-                             f.readline().strip())
+            self.assertEqual(get('3.0', '3.end'), f.readline().strip())
 
 
 if __name__ == '__main__':
index d4566cd..d85278a 100644 (file)
@@ -165,7 +165,7 @@ def overrideRootMenu(root, flist):
         "Handle Help 'About IDLE' event."
         # Synchronize with editor.EditorWindow.about_dialog.
         from idlelib import help_about
-        help_about.AboutDialog(root, 'About IDLE')
+        help_about.AboutDialog(root)
 
     def config_dialog(event=None):
         "Handle Options 'Configure IDLE' event."
index 65345cd..d1dcb83 100644 (file)
@@ -52,6 +52,11 @@ menudefs = [
    ('Find in Files...', '<<find-in-files>>'),
    ('R_eplace...', '<<replace>>'),
    ('Go to _Line', '<<goto-line>>'),
+   ('S_how Completions', '<<force-open-completions>>'),
+   ('E_xpand Word', '<<expand-word>>'),
+   ('Show C_all Tip', '<<force-open-calltip>>'),
+   ('Show Surrounding P_arens', '<<flash-paren>>'),
+
   ]),
 ('format', [
    ('_Indent Region', '<<indent-region>>'),
@@ -62,9 +67,13 @@ menudefs = [
    ('Untabify Region', '<<untabify-region>>'),
    ('Toggle Tabs', '<<toggle-tabs>>'),
    ('New Indent Width', '<<change-indentwidth>>'),
+   ('F_ormat Paragraph', '<<format-paragraph>>'),
+   ('S_trip Trailing Whitespace', '<<do-rstrip>>'),
    ]),
  ('run', [
    ('Python Shell', '<<open-python-shell>>'),
+   ('C_heck Module', '<<check-module>>'),
+   ('R_un Module', '<<run-module>>'),
    ]),
  ('shell', [
    ('_View Last Restart', '<<view-restart>>'),
@@ -80,7 +89,10 @@ menudefs = [
    ]),
  ('options', [
    ('Configure _IDLE', '<<open-config-dialog>>'),
-   None,
+   ('_Code Context', '<<toggle-code-context>>'),
+   ]),
+ ('windows', [
+   ('Zoom Height', '<<zoom-height>>'),
    ]),
  ('help', [
    ('_About IDLE', '<<about-idle>>'),
index f6d2915..6c2a792 100644 (file)
+"""Editor window that can serve as an output file.
+"""
+
 import re
 
-from tkinter import *
-import tkinter.messagebox as tkMessageBox
+from tkinter import messagebox
 
 from idlelib.editor import EditorWindow
 from idlelib import iomenu
 
 
-class OutputWindow(EditorWindow):
+file_line_pats = [
+    # order of patterns matters
+    r'file "([^"]*)", line (\d+)',
+    r'([^\s]+)\((\d+)\)',
+    r'^(\s*\S.*?):\s*(\d+):',  # Win filename, maybe starting with spaces
+    r'([^\s]+):\s*(\d+):',     # filename or path, ltrim
+    r'^\s*(\S.*?):\s*(\d+):',  # Win abs path with embedded spaces, ltrim
+]
+
+file_line_progs = None
+
+
+def compile_progs():
+    "Compile the patterns for matching to file name and line number."
+    global file_line_progs
+    file_line_progs = [re.compile(pat, re.IGNORECASE)
+                       for pat in file_line_pats]
+
 
+def file_line_helper(line):
+    """Extract file name and line number from line of text.
+
+    Check if line of text contains one of the file/line patterns.
+    If it does and if the file and line are valid, return
+    a tuple of the file name and line number.  If it doesn't match
+    or if the file or line is invalid, return None.
+    """
+    if not file_line_progs:
+        compile_progs()
+    for prog in file_line_progs:
+        match = prog.search(line)
+        if match:
+            filename, lineno = match.group(1, 2)
+            try:
+                f = open(filename, "r")
+                f.close()
+                break
+            except OSError:
+                continue
+    else:
+        return None
+    try:
+        return filename, int(lineno)
+    except TypeError:
+        return None
+
+
+class OutputWindow(EditorWindow):
     """An editor window that can serve as an output file.
 
     Also the future base class for the Python shell window.
     This class has no input facilities.
+
+    Adds binding to open a file at a line to the text widget.
     """
 
+    # Our own right-button menu
+    rmenu_specs = [
+        ("Cut", "<<cut>>", "rmenu_check_cut"),
+        ("Copy", "<<copy>>", "rmenu_check_copy"),
+        ("Paste", "<<paste>>", "rmenu_check_paste"),
+        (None, None, None),
+        ("Go to file/line", "<<goto-file-line>>", None),
+    ]
+
     def __init__(self, *args):
         EditorWindow.__init__(self, *args)
         self.text.bind("<<goto-file-line>>", self.goto_file_line)
+        self.text.unbind("<<toggle-code-context>>")
 
     # Customize EditorWindow
-
     def ispythonsource(self, filename):
-        # No colorization needed
-        return 0
+        "Python source is only part of output: do not colorize."
+        return False
 
     def short_title(self):
+        "Customize EditorWindow title."
         return "Output"
 
     def maybesave(self):
-        # Override base class method -- don't ask any questions
-        if self.get_saved():
-            return "yes"
-        else:
-            return "no"
+        "Customize EditorWindow to not display save file messagebox."
+        return 'yes' if self.get_saved() else 'no'
 
     # Act as output file
-
     def write(self, s, tags=(), mark="insert"):
+        """Write text to text widget.
+
+        The text is inserted at the given index with the provided
+        tags.  The text widget is then scrolled to make it visible
+        and updated to display it, giving the effect of seeing each
+        line as it is added.
+
+        Args:
+            s: Text to insert into text widget.
+            tags: Tuple of tag strings to apply on the insert.
+            mark: Index for the insert.
+
+        Return:
+            Length of text inserted.
+        """
         if isinstance(s, (bytes, bytes)):
             s = s.decode(iomenu.encoding, "replace")
         self.text.insert(mark, s, tags)
@@ -46,80 +117,46 @@ class OutputWindow(EditorWindow):
         return len(s)
 
     def writelines(self, lines):
+        "Write each item in lines iterable."
         for line in lines:
             self.write(line)
 
     def flush(self):
+        "No flushing needed as write() directly writes to widget."
         pass
 
-    # Our own right-button menu
-
-    rmenu_specs = [
-        ("Cut", "<<cut>>", "rmenu_check_cut"),
-        ("Copy", "<<copy>>", "rmenu_check_copy"),
-        ("Paste", "<<paste>>", "rmenu_check_paste"),
-        (None, None, None),
-        ("Go to file/line", "<<goto-file-line>>", None),
-    ]
+    def showerror(self, *args, **kwargs):
+        messagebox.showerror(*args, **kwargs)
 
-    file_line_pats = [
-        # order of patterns matters
-        r'file "([^"]*)", line (\d+)',
-        r'([^\s]+)\((\d+)\)',
-        r'^(\s*\S.*?):\s*(\d+):',  # Win filename, maybe starting with spaces
-        r'([^\s]+):\s*(\d+):',     # filename or path, ltrim
-        r'^\s*(\S.*?):\s*(\d+):',  # Win abs path with embedded spaces, ltrim
-    ]
+    def goto_file_line(self, event=None):
+        """Handle request to open file/line.
 
-    file_line_progs = None
+        If the selected or previous line in the output window
+        contains a file name and line number, then open that file
+        name in a new window and position on the line number.
 
-    def goto_file_line(self, event=None):
-        if self.file_line_progs is None:
-            l = []
-            for pat in self.file_line_pats:
-                l.append(re.compile(pat, re.IGNORECASE))
-            self.file_line_progs = l
-        # x, y = self.event.x, self.event.y
-        # self.text.mark_set("insert", "@%d,%d" % (x, y))
+        Otherwise, display an error messagebox.
+        """
         line = self.text.get("insert linestart", "insert lineend")
-        result = self._file_line_helper(line)
+        result = file_line_helper(line)
         if not result:
             # Try the previous line.  This is handy e.g. in tracebacks,
             # where you tend to right-click on the displayed source line
             line = self.text.get("insert -1line linestart",
                                  "insert -1line lineend")
-            result = self._file_line_helper(line)
+            result = file_line_helper(line)
             if not result:
-                tkMessageBox.showerror(
+                self.showerror(
                     "No special line",
                     "The line you point at doesn't look like "
                     "a valid file name followed by a line number.",
                     parent=self.text)
                 return
         filename, lineno = result
-        edit = self.flist.open(filename)
-        edit.gotoline(lineno)
-
-    def _file_line_helper(self, line):
-        for prog in self.file_line_progs:
-            match = prog.search(line)
-            if match:
-                filename, lineno = match.group(1, 2)
-                try:
-                    f = open(filename, "r")
-                    f.close()
-                    break
-                except OSError:
-                    continue
-        else:
-            return None
-        try:
-            return filename, int(lineno)
-        except TypeError:
-            return None
+        self.flist.gotofileline(filename, lineno)
 
-# These classes are currently not used but might come in handy
 
+# These classes are currently not used but might come in handy
 class OnDemandOutputWindow:
 
     tagdefs = {
@@ -145,3 +182,7 @@ class OnDemandOutputWindow:
                 text.tag_configure(tag, **cnf)
         text.tag_raise('sel')
         self.write = self.owin.write
+
+if __name__ == '__main__':
+    import unittest
+    unittest.main('idlelib.idle_test.test_outwin', verbosity=2, exit=False)
index f11bdae..cf8dfdb 100644 (file)
@@ -1,4 +1,4 @@
-"""Extension to format a paragraph or selection to a max width.
+"""Format a paragraph, comment block, or selection to a max width.
 
 Does basic, standard text formatting, and also understands Python
 comment blocks. Thus, for editing Python source code, this
@@ -21,15 +21,14 @@ from idlelib.config import idleConf
 
 class FormatParagraph:
 
-    menudefs = [
-        ('format', [   # /s/edit/format   dscherer@cmu.edu
-            ('Format Paragraph', '<<format-paragraph>>'),
-         ])
-    ]
-
     def __init__(self, editwin):
         self.editwin = editwin
 
+    @classmethod
+    def reload(cls):
+        cls.max_width = idleConf.GetOption('extensions', 'FormatParagraph',
+                                           'max-width', type='int', default=72)
+
     def close(self):
         self.editwin = None
 
@@ -45,11 +44,7 @@ class FormatParagraph:
 
         The length limit parameter is for testing with a known value.
         """
-        if limit is None:
-            # The default length limit is that defined by pep8
-            limit = idleConf.GetOption(
-                'extensions', 'FormatParagraph', 'max-width',
-                type='int', default=72)
+        limit = self.max_width if limit is None else limit
         text = self.editwin.text
         first, last = self.editwin.get_selection_indices()
         if first and last:
@@ -75,6 +70,9 @@ class FormatParagraph:
         text.see("insert")
         return "break"
 
+
+FormatParagraph.reload()
+
 def find_paragraph(text, mark):
     """Returns the start/stop indices enclosing the paragraph that mark is in.
 
index ccec708..983ca20 100644 (file)
@@ -1,4 +1,4 @@
-"""ParenMatch -- An IDLE extension for parenthesis matching.
+"""ParenMatch -- for parenthesis matching.
 
 When you hit a right paren, the cursor should move briefly to the left
 paren.  Paren here is used generically; the matching applies to
@@ -11,43 +11,25 @@ _openers = {')':'(',']':'[','}':'{'}
 CHECK_DELAY = 100 # milliseconds
 
 class ParenMatch:
-    """Highlight matching parentheses
+    """Highlight matching openers and closers, (), [], and {}.
 
-    There are three supported style of paren matching, based loosely
-    on the Emacs options.  The style is select based on the
-    HILITE_STYLE attribute; it can be changed used the set_style
-    method.
+    There are three supported styles of paren matching.  When a right
+    paren (opener) is typed:
 
-    The supported styles are:
+    opener -- highlight the matching left paren (closer);
+    parens -- highlight the left and right parens (opener and closer);
+    expression -- highlight the entire expression from opener to closer.
+    (For back compatibility, 'default' is a synonym for 'opener').
 
-    default -- When a right paren is typed, highlight the matching
-        left paren for 1/2 sec.
-
-    expression -- When a right paren is typed, highlight the entire
-        expression from the left paren to the right paren.
+    Flash-delay is the maximum milliseconds the highlighting remains.
+    Any cursor movement (key press or click) before that removes the
+    highlight.  If flash-delay is 0, there is no maximum.
 
     TODO:
-        - extend IDLE with configuration dialog to change options
-        - implement rest of Emacs highlight styles (see below)
-        - print mismatch warning in IDLE status window
-
-    Note: In Emacs, there are several styles of highlight where the
-    matching paren is highlighted whenever the cursor is immediately
-    to the right of a right paren.  I don't know how to do that in Tk,
-    so I haven't bothered.
+    - Augment bell() with mismatch warning in status window.
+    - Highlight when cursor is moved to the right of a closer.
+      This might be too expensive to check.
     """
-    menudefs = [
-        ('edit', [
-            ("Show surrounding parens", "<<flash-paren>>"),
-        ])
-    ]
-    STYLE = idleConf.GetOption('extensions','ParenMatch','style',
-            default='expression')
-    FLASH_DELAY = idleConf.GetOption('extensions','ParenMatch','flash-delay',
-            type='int',default=500)
-    HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(),'hilite')
-    BELL = idleConf.GetOption('extensions','ParenMatch','bell',
-            type='bool',default=1)
 
     RESTORE_VIRTUAL_EVENT_NAME = "<<parenmatch-check-restore>>"
     # We want the restore event be called before the usual return and
@@ -63,44 +45,44 @@ class ParenMatch:
         # and deactivate_restore (which calls event_delete).
         editwin.text.bind(self.RESTORE_VIRTUAL_EVENT_NAME,
                           self.restore_event)
-        self.bell = self.text.bell if self.BELL else lambda: None
         self.counter = 0
         self.is_restore_active = 0
-        self.set_style(self.STYLE)
+
+    @classmethod
+    def reload(cls):
+        cls.STYLE = idleConf.GetOption(
+            'extensions','ParenMatch','style', default='opener')
+        cls.FLASH_DELAY = idleConf.GetOption(
+                'extensions','ParenMatch','flash-delay', type='int',default=500)
+        cls.BELL = idleConf.GetOption(
+                'extensions','ParenMatch','bell', type='bool', default=1)
+        cls.HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(),
+                                                  'hilite')
 
     def activate_restore(self):
+        "Activate mechanism to restore text from highlighting."
         if not self.is_restore_active:
             for seq in self.RESTORE_SEQUENCES:
                 self.text.event_add(self.RESTORE_VIRTUAL_EVENT_NAME, seq)
             self.is_restore_active = True
 
     def deactivate_restore(self):
+        "Remove restore event bindings."
         if self.is_restore_active:
             for seq in self.RESTORE_SEQUENCES:
                 self.text.event_delete(self.RESTORE_VIRTUAL_EVENT_NAME, seq)
             self.is_restore_active = False
 
-    def set_style(self, style):
-        self.STYLE = style
-        if style == "default":
-            self.create_tag = self.create_tag_default
-            self.set_timeout = self.set_timeout_last
-        elif style == "expression":
-            self.create_tag = self.create_tag_expression
-            self.set_timeout = self.set_timeout_none
-
     def flash_paren_event(self, event):
+        "Handle editor 'show surrounding parens' event (menu or shortcut)."
         indices = (HyperParser(self.editwin, "insert")
                    .get_surrounding_brackets())
-        if indices is None:
-            self.bell()
-            return
-        self.activate_restore()
-        self.create_tag(indices)
-        self.set_timeout_last()
+        self.finish_paren_event(indices)
+        return "break"
 
     def paren_closed_event(self, event):
-        # If it was a shortcut and not really a closing paren, quit.
+        "Handle user input of closer."
+        # If user bound non-closer to <<paren-closed>>, quit.
         closer = self.text.get("insert-1c")
         if closer not in _openers:
             return
@@ -108,14 +90,22 @@ class ParenMatch:
         if not hp.is_in_code():
             return
         indices = hp.get_surrounding_brackets(_openers[closer], True)
-        if indices is None:
-            self.bell()
+        self.finish_paren_event(indices)
+        return  # Allow calltips to see ')'
+
+    def finish_paren_event(self, indices):
+        if indices is None and self.BELL:
+            self.text.bell()
             return
         self.activate_restore()
-        self.create_tag(indices)
-        self.set_timeout()
+        # self.create_tag(indices)
+        self.tagfuncs.get(self.STYLE, self.create_tag_expression)(self, indices)
+        # self.set_timeout()
+        (self.set_timeout_last if self.FLASH_DELAY else
+                            self.set_timeout_none)()
 
     def restore_event(self, event=None):
+        "Remove effect of doing match."
         self.text.tag_delete("paren")
         self.deactivate_restore()
         self.counter += 1   # disable the last timer, if there is one.
@@ -127,11 +117,20 @@ class ParenMatch:
     # any one of the create_tag_XXX methods can be used depending on
     # the style
 
-    def create_tag_default(self, indices):
+    def create_tag_opener(self, indices):
         """Highlight the single paren that matches"""
         self.text.tag_add("paren", indices[0])
         self.text.tag_config("paren", self.HILITE_CONFIG)
 
+    def create_tag_parens(self, indices):
+        """Highlight the left and right parens"""
+        if self.text.get(indices[1]) in (')', ']', '}'):
+            rightindex = indices[1]+"+1c"
+        else:
+            rightindex = indices[1]
+        self.text.tag_add("paren", indices[0], indices[0]+"+1c", rightindex+"-1c", rightindex)
+        self.text.tag_config("paren", self.HILITE_CONFIG)
+
     def create_tag_expression(self, indices):
         """Highlight the entire expression"""
         if self.text.get(indices[1]) in (')', ']', '}'):
@@ -141,6 +140,13 @@ class ParenMatch:
         self.text.tag_add("paren", indices[0], rightindex)
         self.text.tag_config("paren", self.HILITE_CONFIG)
 
+    tagfuncs = {
+        'opener': create_tag_opener,
+        'default': create_tag_opener,
+        'parens': create_tag_parens,
+        'expression': create_tag_expression,
+        }
+
     # any one of the set_timeout_XXX methods can be used depending on
     # the style
 
@@ -160,7 +166,7 @@ class ParenMatch:
         self.editwin.text_frame.after(CHECK_DELAY, callme, callme)
 
     def set_timeout_last(self):
-        """The last highlight created will be removed after .5 sec"""
+        """The last highlight created will be removed after FLASH_DELAY millisecs"""
         # associate a counter with an event; only disable the "paren"
         # tag if the event is for the most recent timer.
         self.counter += 1
@@ -169,6 +175,9 @@ class ParenMatch:
             lambda self=self, c=self.counter: self.handle_restore_timer(c))
 
 
+ParenMatch.reload()
+
+
 if __name__ == '__main__':
     import unittest
     unittest.main('idlelib.idle_test.test_parenmatch', verbosity=2)
index 5b0e5b2..47df744 100755 (executable)
@@ -117,8 +117,8 @@ class PyShellEditorWindow(EditorWindow):
         self.text.bind("<<clear-breakpoint-here>>", self.clear_breakpoint_here)
         self.text.bind("<<open-python-shell>>", self.flist.open_shell)
 
-        self.breakpointPath = os.path.join(idleConf.GetUserCfgDir(),
-                                           'breakpoints.lst')
+        self.breakpointPath = os.path.join(
+                idleConf.userdir, 'breakpoints.lst')
         # whenever a file is changed, restore breakpoints
         def filename_changed_hook(old_hook=self.io.filename_change_hook,
                                   self=self):
@@ -892,7 +892,7 @@ class PyShell(OutputWindow):
         try:
             # page help() text to shell.
             import pydoc # import must be done here to capture i/o rebinding.
-            # XXX KBK 27Dec07 use TextViewer someday, but must work w/o subproc
+            # XXX KBK 27Dec07 use text viewer someday, but must work w/o subproc
             pydoc.pager = pydoc.plainpager
         except:
             sys.stderr = sys.__stderr__
index 2ce3c7e..18c86f9 100644 (file)
@@ -2,12 +2,8 @@
 
 class RstripExtension:
 
-    menudefs = [
-        ('format', [None, ('Strip trailing whitespace', '<<do-rstrip>>'), ] ), ]
-
     def __init__(self, editwin):
         self.editwin = editwin
-        self.editwin.text.bind("<<do-rstrip>>", self.do_rstrip)
 
     def do_rstrip(self, event=None):
 
index 79d86ad..45bf563 100644 (file)
@@ -1,22 +1,14 @@
-"""Extension to execute code outside the Python shell window.
+"""Execute code from an editor.
 
-This adds the following commands:
+Check module: do a full syntax check of the current module.
+Also run the tabnanny to catch any inconsistent tabs.
 
-- Check module does a full syntax check of the current module.
-  It also runs the tabnanny to catch any inconsistent tabs.
-
-- Run module executes the module's code in the __main__ namespace.  The window
-  must have been saved previously. The module is added to sys.modules, and is
-  also added to the __main__ namespace.
-
-XXX GvR Redesign this interface (yet again) as follows:
-
-- Present a dialog box for ``Run Module''
-
-- Allow specify command line arguments in the dialog box
+Run module: also execute the module's code in the __main__ namespace.
+The window must have been saved previously. The module is added to
+sys.modules, and is also added to the __main__ namespace.
 
+TODO: Specify command line arguments in a dialog box.
 """
-
 import os
 import tabnanny
 import tokenize
@@ -40,11 +32,6 @@ by Format->Untabify Region and specify the number of columns used by each tab.
 
 class ScriptBinding:
 
-    menudefs = [
-        ('run', [None,
-                 ('Check Module', '<<check-module>>'),
-                 ('Run Module', '<<run-module>>'), ]), ]
-
     def __init__(self, editwin):
         self.editwin = editwin
         # Provide instance variables referenced by debugger
@@ -63,6 +50,7 @@ class ScriptBinding:
             return 'break'
         if not self.tabnanny(filename):
             return 'break'
+        return "break"
 
     def tabnanny(self, filename):
         # XXX: tabnanny should work on binary files as well
index cc08c26..cdf6584 100644 (file)
@@ -76,6 +76,7 @@ class ScrolledList:
         index = self.listbox.index("active")
         self.select(index)
         menu.tk_popup(event.x_root, event.y_root)
+        return "break"
 
     def make_menu(self):
         menu = Menu(self.listbox, tearoff=0)
index ab653a9..de4b190 100644 (file)
@@ -1,14 +1,59 @@
 """Simple text browser for IDLE
 
 """
-from tkinter import Toplevel, Frame, Button, Text
-from tkinter import DISABLED, SUNKEN, VERTICAL, WORD
-from tkinter import RIGHT, LEFT, TOP, BOTTOM, BOTH, X, Y
-from tkinter.ttk import Scrollbar
+from tkinter import Toplevel, Text
+from tkinter.ttk import Frame, Scrollbar, Button
 from tkinter.messagebox import showerror
 
 
-class TextViewer(Toplevel):
+class TextFrame(Frame):
+    "Display text with scrollbar."
+
+    def __init__(self, parent, rawtext):
+        """Create a frame for Textview.
+
+        parent - parent widget for this frame
+        rawtext - text to display
+        """
+        super().__init__(parent)
+        self['relief'] = 'sunken'
+        self['height'] = 700
+        # TODO: get fg/bg from theme.
+        self.bg = '#ffffff'
+        self.fg = '#000000'
+
+        self.text = text = Text(self, wrap='word', highlightthickness=0,
+                                fg=self.fg, bg=self.bg)
+        self.scroll = scroll = Scrollbar(self, orient='vertical',
+                                         takefocus=False, command=text.yview)
+        text['yscrollcommand'] = scroll.set
+        text.insert(0.0, rawtext)
+        text['state'] = 'disabled'
+        text.focus_set()
+
+        scroll.pack(side='right', fill='y')
+        text.pack(side='left', expand=True, fill='both')
+
+
+class ViewFrame(Frame):
+    "Display TextFrame and Close button."
+    def __init__(self, parent, text):
+        super().__init__(parent)
+        self.parent = parent
+        self.bind('<Return>', self.ok)
+        self.bind('<Escape>', self.ok)
+        self.textframe = TextFrame(self, text)
+        self.button_ok = button_ok = Button(
+                self, text='Close', command=self.ok, takefocus=False)
+        self.textframe.pack(side='top', expand=True, fill='both')
+        button_ok.pack(side='bottom')
+
+    def ok(self, event=None):
+        """Dismiss text viewer dialog."""
+        self.parent.destroy()
+
+
+class ViewWindow(Toplevel):
     "A simple text viewer dialog for IDLE."
 
     def __init__(self, parent, title, text, modal=True,
@@ -24,26 +69,19 @@ class TextViewer(Toplevel):
         _htest - bool; change box location when running htest.
         _utest - bool; don't wait_window when running unittest.
         """
-        Toplevel.__init__(self, parent)
-        self.configure(borderwidth=5)
+        super().__init__(parent)
+        self['borderwidth'] = 5
         # Place dialog below parent if running htest.
-        self.geometry("=%dx%d+%d+%d" % (750, 500,
-                           parent.winfo_rootx() + 10,
-                           parent.winfo_rooty() + (10 if not _htest else 100)))
-        # TODO: get fg/bg from theme.
-        self.bg = '#ffffff'
-        self.fg = '#000000'
+        x = parent.winfo_rootx() + 10
+        y = parent.winfo_rooty() + (10 if not _htest else 100)
+        self.geometry(f'=750x500+{x}+{y}')
 
-        self.create_widgets()
         self.title(title)
+        self.viewframe = ViewFrame(self, text)
         self.protocol("WM_DELETE_WINDOW", self.ok)
-        self.parent = parent
-        self.text.focus_set()
-        # Bind keys for closing this dialog.
-        self.bind('<Return>', self.ok)
-        self.bind('<Escape>', self.ok)
-        self.text.insert(0.0, text)
-        self.text.config(state=DISABLED)
+        self.button_ok = button_ok = Button(self, text='Close',
+                                            command=self.ok, takefocus=False)
+        self.viewframe.pack(side='top', expand=True, fill='both')
 
         if modal:
             self.transient(parent)
@@ -51,31 +89,13 @@ class TextViewer(Toplevel):
             if not _utest:
                 self.wait_window()
 
-    def create_widgets(self):
-        "Create Frame with Text (with vertical Scrollbar) and Button."
-        frame = Frame(self, relief=SUNKEN, height=700)
-        frame_buttons = Frame(self)
-        self.button_ok = Button(frame_buttons, text='Close',
-                                command=self.ok, takefocus=False)
-        self.scrollbar = Scrollbar(frame, orient=VERTICAL, takefocus=False)
-        self.text = Text(frame, wrap=WORD, highlightthickness=0,
-                             fg=self.fg, bg=self.bg)
-        self.scrollbar.config(command=self.text.yview)
-        self.text.config(yscrollcommand=self.scrollbar.set)
-        
-        self.button_ok.pack()
-        self.scrollbar.pack(side=RIGHT, fill=Y)
-        self.text.pack(side=LEFT, expand=True, fill=BOTH)
-        frame_buttons.pack(side=BOTTOM, fill=X)
-        frame.pack(side=TOP, expand=True, fill=BOTH)
-
     def ok(self, event=None):
         """Dismiss text viewer dialog."""
         self.destroy()
 
 
 def view_text(parent, title, text, modal=True, _utest=False):
-    """Create TextViewer for given text.
+    """Create text viewer for given text.
 
     parent - parent of this dialog
     title - string which is the title of popup dialog
@@ -84,11 +104,11 @@ def view_text(parent, title, text, modal=True, _utest=False):
             dialog is displayed
     _utest - bool; controls wait_window on unittest
     """
-    return TextViewer(parent, title, text, modal, _utest=_utest)
+    return ViewWindow(parent, title, text, modal, _utest=_utest)
 
 
 def view_file(parent, title, filename, encoding=None, modal=True, _utest=False):
-    """Create TextViewer for text in filename.
+    """Create text viewer for text in filename.
 
     Return error message if file cannot be read.  Otherwise calls view_text
     with contents of the file.
@@ -98,7 +118,7 @@ def view_file(parent, title, filename, encoding=None, modal=True, _utest=False):
             contents = file.read()
     except OSError:
         showerror(title='File Load Error',
-                  message='Unable to load file %r .' % filename,
+                  message=f'Unable to load file {filename!r} .',
                   parent=parent)
     except UnicodeDecodeError as err:
         showerror(title='Unicode Decode Error',
@@ -113,4 +133,4 @@ if __name__ == '__main__':
     import unittest
     unittest.main('idlelib.idle_test.test_textview', verbosity=2, exit=False)
     from idlelib.idle_test.htest import run
-    run(TextViewer)
+    run(ViewWindow)
index aa4a427..74fbc88 100644 (file)
@@ -1,4 +1,4 @@
-# Sample extension: zoom a window to maximum height
+"Zoom a window to maximum height."
 
 import re
 import sys
@@ -8,18 +8,13 @@ from idlelib import macosx
 
 class ZoomHeight:
 
-    menudefs = [
-        ('windows', [
-            ('_Zoom Height', '<<zoom-height>>'),
-         ])
-    ]
-
     def __init__(self, editwin):
         self.editwin = editwin
 
     def zoom_height_event(self, event):
         top = self.editwin.top
         zoom_height(top)
+        return "break"
 
 
 def zoom_height(top):
diff --git a/Lib/idlelib/zzdummy.py b/Lib/idlelib/zzdummy.py
new file mode 100644 (file)
index 0000000..8084499
--- /dev/null
@@ -0,0 +1,42 @@
+"Example extension, also used for testing."
+
+from idlelib.config import idleConf
+
+ztext = idleConf.GetOption('extensions', 'ZzDummy', 'z-text')
+
+
+class ZzDummy:
+
+##    menudefs = [
+##        ('format', [
+##            ('Z in', '<<z-in>>'),
+##            ('Z out', '<<z-out>>'),
+##        ] )
+##    ]
+
+    def __init__(self, editwin):
+        self.text = editwin.text
+        z_in = False
+
+    @classmethod
+    def reload(cls):
+        cls.ztext = idleConf.GetOption('extensions', 'ZzDummy', 'z-text')
+
+    def z_in_event(self, event):
+        """
+        """
+        text = self.text
+        text.undo_block_start()
+        for line in range(1, text.index('end')):
+            text.insert('%d.0', ztest)
+        text.undo_block_stop()
+        return "break"
+
+    def z_out_event(self, event): pass
+
+ZzDummy.reload()
+
+##if __name__ == "__main__":
+##    import unittest
+##    unittest.main('idlelib.idle_test.test_zzdummy',
+##            verbosity=2, exit=False)
index 781ff23..866464b 100644 (file)
@@ -203,8 +203,9 @@ def load_package(name, path):
         extensions = (machinery.SOURCE_SUFFIXES[:] +
                       machinery.BYTECODE_SUFFIXES[:])
         for extension in extensions:
-            path = os.path.join(path, '__init__'+extension)
-            if os.path.exists(path):
+            init_path = os.path.join(path, '__init__' + extension)
+            if os.path.exists(init_path):
+                path = init_path
                 break
         else:
             raise ValueError('{!r} is not a package'.format(path))
index a531a03..4cf8aec 100644 (file)
@@ -39,6 +39,7 @@ def _new_module(name):
 # Module-level locking ########################################################
 
 # A dict mapping module names to weakrefs of _ModuleLock instances
+# Dictionary protected by the global import lock
 _module_locks = {}
 # A dict mapping thread ids to _ModuleLock instances
 _blocking_on = {}
@@ -144,10 +145,7 @@ class _ModuleLockManager:
         self._lock = None
 
     def __enter__(self):
-        try:
-            self._lock = _get_module_lock(self._name)
-        finally:
-            _imp.release_lock()
+        self._lock = _get_module_lock(self._name)
         self._lock.acquire()
 
     def __exit__(self, *args, **kwargs):
@@ -159,31 +157,47 @@ class _ModuleLockManager:
 def _get_module_lock(name):
     """Get or create the module lock for a given module name.
 
-    Should only be called with the import lock taken."""
-    lock = None
+    Acquire/release internally the global import lock to protect
+    _module_locks."""
+
+    _imp.acquire_lock()
     try:
-        lock = _module_locks[name]()
-    except KeyError:
-        pass
-    if lock is None:
-        if _thread is None:
-            lock = _DummyModuleLock(name)
-        else:
-            lock = _ModuleLock(name)
-        def cb(_):
-            del _module_locks[name]
-        _module_locks[name] = _weakref.ref(lock, cb)
+        try:
+            lock = _module_locks[name]()
+        except KeyError:
+            lock = None
+
+        if lock is None:
+            if _thread is None:
+                lock = _DummyModuleLock(name)
+            else:
+                lock = _ModuleLock(name)
+
+            def cb(ref, name=name):
+                _imp.acquire_lock()
+                try:
+                    # bpo-31070: Check if another thread created a new lock
+                    # after the previous lock was destroyed
+                    # but before the weakref callback was called.
+                    if _module_locks.get(name) is ref:
+                        del _module_locks[name]
+                finally:
+                    _imp.release_lock()
+
+            _module_locks[name] = _weakref.ref(lock, cb)
+    finally:
+        _imp.release_lock()
+
     return lock
 
+
 def _lock_unlock_module(name):
-    """Release the global import lock, and acquires then release the
-    module lock for a given module name.
+    """Acquires then releases the module lock for a given module name.
+
     This is used to ensure a module is completely initialized, in the
     event it is being imported by another thread.
-
-    Should only be called with the import lock taken."""
+    """
     lock = _get_module_lock(name)
-    _imp.release_lock()
     try:
         lock.acquire()
     except _DeadlockError:
@@ -442,9 +456,6 @@ def spec_from_loader(name, loader, *, origin=None, is_package=None):
     return ModuleSpec(name, loader, origin=origin, is_package=is_package)
 
 
-_POPULATE = object()
-
-
 def _spec_from_module(module, loader=None, origin=None):
     # This function is meant for use in _setup().
     try:
@@ -587,7 +598,6 @@ def _module_repr_from_spec(spec):
 def _exec(spec, module):
     """Execute the spec's specified module in an existing module's namespace."""
     name = spec.name
-    _imp.acquire_lock()
     with _ModuleLockManager(name):
         if sys.modules.get(name) is not module:
             msg = 'module {!r} not in sys.modules'.format(name)
@@ -670,7 +680,6 @@ def _load(spec):
     clobbered.
 
     """
-    _imp.acquire_lock()
     with _ModuleLockManager(spec.name):
         return _load_unlocked(spec)
 
@@ -917,10 +926,6 @@ def _sanity_check(name, package, level):
         elif not package:
             raise ImportError('attempted relative import with no known parent '
                               'package')
-        elif package not in sys.modules:
-            msg = ('Parent module {!r} not loaded, cannot perform relative '
-                   'import')
-            raise SystemError(msg.format(package))
     if not name and level == 0:
         raise ValueError('Empty module name')
 
@@ -955,10 +960,23 @@ def _find_and_load_unlocked(name, import_):
     return module
 
 
+_NEEDS_LOADING = object()
+
+
 def _find_and_load(name, import_):
-    """Find and load the module, and release the import lock."""
+    """Find and load the module."""
     with _ModuleLockManager(name):
-        return _find_and_load_unlocked(name, import_)
+        module = sys.modules.get(name, _NEEDS_LOADING)
+        if module is _NEEDS_LOADING:
+            return _find_and_load_unlocked(name, import_)
+
+    if module is None:
+        message = ('import of {} halted; '
+                   'None in sys.modules'.format(name))
+        raise ModuleNotFoundError(message, name=name)
+
+    _lock_unlock_module(name)
+    return module
 
 
 def _gcd_import(name, package=None, level=0):
@@ -973,17 +991,7 @@ def _gcd_import(name, package=None, level=0):
     _sanity_check(name, package, level)
     if level > 0:
         name = _resolve_name(name, package, level)
-    _imp.acquire_lock()
-    if name not in sys.modules:
-        return _find_and_load(name, _gcd_import)
-    module = sys.modules[name]
-    if module is None:
-        _imp.release_lock()
-        message = ('import of {} halted; '
-                   'None in sys.modules'.format(name))
-        raise ModuleNotFoundError(message, name=name)
-    _lock_unlock_module(name)
-    return module
+    return _find_and_load(name, _gcd_import)
 
 
 def _handle_fromlist(module, fromlist, import_):
index b8d5e6c..6d0511e 100644 (file)
@@ -76,7 +76,7 @@ Specializing JSON object encoding::
     >>> def encode_complex(obj):
     ...     if isinstance(obj, complex):
     ...         return [obj.real, obj.imag]
-    ...     raise TypeError(repr(o) + " is not JSON serializable")
+    ...     raise TypeError(repr(obj) + " is not JSON serializable")
     ...
     >>> json.dumps(2 + 1j, default=encode_complex)
     '[2.0, 1.0]'
index b44a3b2..0965536 100644 (file)
@@ -1697,6 +1697,27 @@ class LoggerAdapter(object):
         """
         return self.logger.hasHandlers()
 
+    def _log(self, level, msg, args, exc_info=None, extra=None, stack_info=False):
+        """
+        Low-level log implementation, proxied to allow nested logger adapters.
+        """
+        return self.logger._log(
+            level,
+            msg,
+            args,
+            exc_info=exc_info,
+            extra=extra,
+            stack_info=stack_info,
+        )
+
+    @property
+    def manager(self):
+        return self.logger.manager
+
+    @manager.setter
+    def set_manager(self, value):
+        self.logger.manager = value
+
     def __repr__(self):
         logger = self.logger
         level = getLevelName(logger.getEffectiveLevel())
index 9a88680..5a7e649 100644 (file)
@@ -439,6 +439,7 @@ def _default_mime_types():
         '.jpeg'   : 'image/jpeg',
         '.jpg'    : 'image/jpeg',
         '.js'     : 'application/javascript',
+        '.json'   : 'application/json',
         '.ksh'    : 'text/plain',
         '.latex'  : 'application/x-latex',
         '.m1v'    : 'video/mpeg',
index bca8b7a..1d26b5e 100644 (file)
@@ -104,6 +104,9 @@ class BaseProcess(object):
         _cleanup()
         self._popen = self._Popen(self)
         self._sentinel = self._popen.sentinel
+        # Avoid a refcycle if the target function holds an indirect
+        # reference to the process object (see bpo-30775)
+        del self._target, self._args, self._kwargs
         _children.add(self)
 
     def terminate(self):
@@ -129,10 +132,16 @@ class BaseProcess(object):
         if self is _current_process:
             return True
         assert self._parent_pid == os.getpid(), 'can only test a child process'
+
         if self._popen is None:
             return False
-        self._popen.poll()
-        return self._popen.returncode is None
+
+        returncode = self._popen.poll()
+        if returncode is None:
+            return True
+        else:
+            _children.discard(self)
+            return False
 
     @property
     def name(self):
index 7f77837..513807c 100644 (file)
@@ -101,7 +101,7 @@ class Queue(object):
             try:
                 if block:
                     timeout = deadline - time.time()
-                    if timeout < 0 or not self._poll(timeout):
+                    if not self._poll(timeout):
                         raise Empty
                 elif not self._poll():
                     raise Empty
@@ -169,14 +169,7 @@ class Queue(object):
         self._thread.start()
         debug('... done self._thread.start()')
 
-        # On process exit we will wait for data to be flushed to pipe.
-        #
-        # However, if this process created the queue then all
-        # processes which use the queue will be descendants of this
-        # process.  Therefore waiting for the queue to be flushed
-        # is pointless once all the child processes have been joined.
-        created_by_this_process = (self._opid == os.getpid())
-        if not self._joincancelled and not created_by_this_process:
+        if not self._joincancelled:
             self._jointhread = Finalize(
                 self._thread, Queue._finalize_join,
                 [weakref.ref(self._thread)],
index fa06f39..9fa8acb 100644 (file)
--- a/Lib/os.py
+++ b/Lib/os.py
@@ -685,7 +685,9 @@ class _Environ(MutableMapping):
             raise KeyError(key) from None
 
     def __iter__(self):
-        for key in self._data:
+        # list() from dict object is an atomic operation
+        keys = list(self._data)
+        for key in keys:
             yield self.decodekey(key)
 
     def __len__(self):
index 015afb3..deb540d 100644 (file)
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Autogenerated by Sphinx on Sat Jun 17 04:32:54 2017
+# Autogenerated by Sphinx on Mon Sep 18 23:00:11 2017
 topics = {'assert': 'The "assert" statement\n'
            '**********************\n'
            '\n'
@@ -8558,7 +8558,7 @@ topics = {'assert': 'The "assert" statement\n'
                  '\n'
                  'The potential uses for metaclasses are boundless. Some ideas '
                  'that have\n'
-                 'been explored include logging, interface checking, '
+                 'been explored include enum, logging, interface checking, '
                  'automatic\n'
                  'delegation, automatic property creation, proxies, '
                  'frameworks, and\n'
@@ -10332,27 +10332,22 @@ topics = {'assert': 'The "assert" statement\n'
           '*******************\n'
           '\n'
           'Any object can be tested for truth value, for use in an "if" or\n'
-          '"while" condition or as operand of the Boolean operations below. '
-          'The\n'
-          'following values are considered false:\n'
-          '\n'
-          '* "None"\n'
-          '\n'
-          '* "False"\n'
+          '"while" condition or as operand of the Boolean operations below.\n'
           '\n'
-          '* zero of any numeric type, for example, "0", "0.0", "0j".\n'
-          '\n'
-          '* any empty sequence, for example, "\'\'", "()", "[]".\n'
+          'By default, an object is considered true unless its class defines\n'
+          'either a "__bool__()" method that returns "False" or a "__len__()"\n'
+          'method that returns zero, when called with the object. [1]  Here '
+          'are\n'
+          'most of the built-in objects considered false:\n'
           '\n'
-          '* any empty mapping, for example, "{}".\n'
+          '* constants defined to be false: "None" and "False".\n'
           '\n'
-          '* instances of user-defined classes, if the class defines a\n'
-          '  "__bool__()" or "__len__()" method, when that method returns the\n'
-          '  integer zero or "bool" value "False". [1]\n'
+          '* zero of any numeric type: "0", "0.0", "0j", "Decimal(0)",\n'
+          '  "Fraction(0, 1)"\n'
           '\n'
-          'All other values are considered true --- so objects of many types '
-          'are\n'
-          'always true.\n'
+          '* empty sequences and collections: "\'\'", "()", "[]", "{}", '
+          '"set()",\n'
+          '  "range(0)"\n'
           '\n'
           'Operations and built-in functions that have a Boolean result '
           'always\n'
@@ -12401,7 +12396,7 @@ topics = {'assert': 'The "assert" statement\n'
              'operations.\n'
              '   Lists also provide the following additional method:\n'
              '\n'
-             '   sort(*, key=None, reverse=None)\n'
+             '   sort(*, key=None, reverse=False)\n'
              '\n'
              '      This method sorts the list in place, using only "<" '
              'comparisons\n'
index 740e717..1ada24d 100644 (file)
@@ -711,6 +711,8 @@ def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
             if source_address:
                 sock.bind(source_address)
             sock.connect(sa)
+            # Break explicitly a reference cycle
+            err = None
             return sock
 
         except error as _:
index 8ad4a33..1f3a31a 100644 (file)
@@ -115,7 +115,7 @@ except ImportError:
     pass
 
 
-from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN
+from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN, HAS_TLSv1_3
 from _ssl import _OPENSSL_API_VERSION
 
 
@@ -178,6 +178,7 @@ else:
 # (OpenSSL's default setting is 'DEFAULT:!aNULL:!eNULL')
 # Enable a better set of ciphers by default
 # This list has been explicitly chosen to:
+#   * TLS 1.3 ChaCha20 and AES-GCM cipher suites
 #   * Prefer cipher suites that offer perfect forward secrecy (DHE/ECDHE)
 #   * Prefer ECDHE over DHE for better performance
 #   * Prefer AEAD over CBC for better performance and security
@@ -189,6 +190,8 @@ else:
 #   * Disable NULL authentication, NULL encryption, 3DES and MD5 MACs
 #     for security reasons
 _DEFAULT_CIPHERS = (
+    'TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:'
+    'TLS13-AES-128-GCM-SHA256:'
     'ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:DH+CHACHA20:ECDH+AES256:DH+AES256:'
     'ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:RSA+AESGCM:RSA+AES:RSA+HIGH:'
     '!aNULL:!eNULL:!MD5:!3DES'
@@ -196,6 +199,7 @@ _DEFAULT_CIPHERS = (
 
 # Restricted and more secure ciphers for the server side
 # This list has been explicitly chosen to:
+#   * TLS 1.3 ChaCha20 and AES-GCM cipher suites
 #   * Prefer cipher suites that offer perfect forward secrecy (DHE/ECDHE)
 #   * Prefer ECDHE over DHE for better performance
 #   * Prefer AEAD over CBC for better performance and security
@@ -206,6 +210,8 @@ _DEFAULT_CIPHERS = (
 #   * Disable NULL authentication, NULL encryption, MD5 MACs, DSS, RC4, and
 #     3DES for security reasons
 _RESTRICTED_SERVER_CIPHERS = (
+    'TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:'
+    'TLS13-AES-128-GCM-SHA256:'
     'ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:DH+CHACHA20:ECDH+AES256:DH+AES256:'
     'ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:RSA+AESGCM:RSA+AES:RSA+HIGH:'
     '!aNULL:!eNULL:!MD5:!DSS:!RC4:!3DES'
@@ -959,11 +965,12 @@ class SSLSocket(socket):
                 raise ValueError(
                     "non-zero flags not allowed in calls to sendall() on %s" %
                     self.__class__)
-            amount = len(data)
             count = 0
-            while (count < amount):
-                v = self.send(data[count:])
-                count += v
+            with memoryview(data) as view, view.cast("B") as byte_view:
+                amount = len(byte_view)
+                while count < amount:
+                    v = self.send(byte_view[count:])
+                    count += v
         else:
             return socket.sendall(self, data, flags)
 
index b790cbd..290ae44 100644 (file)
@@ -38,7 +38,7 @@ check_output(...): Same as check_call() but returns the contents of
 getoutput(...): Runs a command in the shell, waits for it to complete,
     then returns the output
 getstatusoutput(...): Runs a command in the shell, waits for it to complete,
-    then returns a (status, output) tuple
+    then returns a (exitcode, output) tuple
 """
 
 import sys
@@ -493,7 +493,7 @@ def list2cmdline(seq):
 #
 
 def getstatusoutput(cmd):
-    """    Return (status, output) of executing cmd in a shell.
+    """Return (exitcode, output) of executing cmd in a shell.
 
     Execute the string 'cmd' in a shell with 'check_output' and
     return a 2-tuple (status, output). The locale encoding is used
@@ -507,19 +507,21 @@ def getstatusoutput(cmd):
     >>> subprocess.getstatusoutput('ls /bin/ls')
     (0, '/bin/ls')
     >>> subprocess.getstatusoutput('cat /bin/junk')
-    (256, 'cat: /bin/junk: No such file or directory')
+    (1, 'cat: /bin/junk: No such file or directory')
     >>> subprocess.getstatusoutput('/bin/junk')
-    (256, 'sh: /bin/junk: not found')
+    (127, 'sh: /bin/junk: not found')
+    >>> subprocess.getstatusoutput('/bin/kill $$')
+    (-15, '')
     """
     try:
         data = check_output(cmd, shell=True, universal_newlines=True, stderr=STDOUT)
-        status = 0
+        exitcode = 0
     except CalledProcessError as ex:
         data = ex.output
-        status = ex.returncode
+        exitcode = ex.returncode
     if data[-1:] == '\n':
         data = data[:-1]
-    return status, data
+    return exitcode, data
 
 def getoutput(cmd):
     """Return output (stdout or stderr) of executing cmd in a shell.
@@ -725,7 +727,10 @@ class Popen(object):
                     to_close.append(self._devnull)
                 for fd in to_close:
                     try:
-                        os.close(fd)
+                        if _mswindows and isinstance(fd, Handle):
+                            fd.Close()
+                        else:
+                            os.close(fd)
                     except OSError:
                         pass
 
@@ -1005,6 +1010,9 @@ class Popen(object):
                     errwrite.Close()
                 if hasattr(self, '_devnull'):
                     os.close(self._devnull)
+                # Prevent a double close of these handles/fds from __init__
+                # on error.
+                self._closed_child_pipe_fds = True
 
             # Retain the process handle, but close the thread handle
             self._child_created = True
@@ -1308,29 +1316,32 @@ class Popen(object):
                 try:
                     exception_name, hex_errno, err_msg = (
                             errpipe_data.split(b':', 2))
+                    # The encoding here should match the encoding
+                    # written in by the subprocess implementations
+                    # like _posixsubprocess
+                    err_msg = err_msg.decode()
                 except ValueError:
                     exception_name = b'SubprocessError'
                     hex_errno = b'0'
-                    err_msg = (b'Bad exception data from child: ' +
-                               repr(errpipe_data))
+                    err_msg = 'Bad exception data from child: {!r}'.format(
+                                  bytes(errpipe_data))
                 child_exception_type = getattr(
                         builtins, exception_name.decode('ascii'),
                         SubprocessError)
-                err_msg = err_msg.decode(errors="surrogatepass")
                 if issubclass(child_exception_type, OSError) and hex_errno:
                     errno_num = int(hex_errno, 16)
                     child_exec_never_called = (err_msg == "noexec")
                     if child_exec_never_called:
                         err_msg = ""
+                        # The error must be from chdir(cwd).
+                        err_filename = cwd
+                    else:
+                        err_filename = orig_executable
                     if errno_num != 0:
                         err_msg = os.strerror(errno_num)
                         if errno_num == errno.ENOENT:
-                            if child_exec_never_called:
-                                # The error must be from chdir(cwd).
-                                err_msg += ': ' + repr(cwd)
-                            else:
-                                err_msg += ': ' + repr(orig_executable)
-                    raise child_exception_type(errno_num, err_msg)
+                            err_msg += ': ' + repr(err_filename)
+                    raise child_exception_type(errno_num, err_msg, err_filename)
                 raise child_exception_type(err_msg)
 
 
index cd2c8db..db30e6b 100644 (file)
@@ -32,11 +32,12 @@ test.support.import_module('multiprocessing.synchronize')
 # without thread support.
 import threading
 
-import multiprocessing.dummy
 import multiprocessing.connection
-import multiprocessing.managers
+import multiprocessing.dummy
 import multiprocessing.heap
+import multiprocessing.managers
 import multiprocessing.pool
+import multiprocessing.queues
 
 from multiprocessing import util
 
@@ -64,6 +65,13 @@ except ImportError:
 def latin(s):
     return s.encode('latin')
 
+
+def close_queue(queue):
+    if isinstance(queue, multiprocessing.queues.Queue):
+        queue.close()
+        queue.join_thread()
+
+
 #
 # Constants
 #
@@ -191,6 +199,12 @@ def get_value(self):
 # Testcases
 #
 
+class DummyCallable:
+    def __call__(self, q, c):
+        assert isinstance(c, DummyCallable)
+        q.put(5)
+
+
 class _TestProcess(BaseTestCase):
 
     ALLOWED_TYPES = ('processes', 'threads')
@@ -269,6 +283,7 @@ class _TestProcess(BaseTestCase):
         self.assertEqual(p.exitcode, 0)
         self.assertEqual(p.is_alive(), False)
         self.assertNotIn(p, self.active_children())
+        close_queue(q)
 
     @classmethod
     def _test_terminate(cls):
@@ -398,6 +413,19 @@ class _TestProcess(BaseTestCase):
         p.join()
         self.assertTrue(wait_for_handle(sentinel, timeout=1))
 
+    def test_lose_target_ref(self):
+        c = DummyCallable()
+        wr = weakref.ref(c)
+        q = self.Queue()
+        p = self.Process(target=c, args=(q, c))
+        del c
+        p.start()
+        p.join()
+        self.assertIs(wr(), None)
+        self.assertEqual(q.get(), 5)
+        close_queue(q)
+
+
 #
 #
 #
@@ -582,6 +610,7 @@ class _TestQueue(BaseTestCase):
         self.assertEqual(queue_full(queue, MAXSIZE), False)
 
         proc.join()
+        close_queue(queue)
 
     @classmethod
     def _test_get(cls, queue, child_can_start, parent_can_continue):
@@ -644,6 +673,7 @@ class _TestQueue(BaseTestCase):
         self.assertTimingAlmostEqual(get.elapsed, TIMEOUT3)
 
         proc.join()
+        close_queue(queue)
 
     @classmethod
     def _test_fork(cls, queue):
@@ -679,6 +709,7 @@ class _TestQueue(BaseTestCase):
         self.assertRaises(pyqueue.Empty, queue.get, False)
 
         p.join()
+        close_queue(queue)
 
     def test_qsize(self):
         q = self.Queue()
@@ -694,6 +725,7 @@ class _TestQueue(BaseTestCase):
         self.assertEqual(q.qsize(), 1)
         q.get()
         self.assertEqual(q.qsize(), 0)
+        close_queue(q)
 
     @classmethod
     def _test_task_done(cls, q):
@@ -721,6 +753,7 @@ class _TestQueue(BaseTestCase):
 
         for p in workers:
             p.join()
+        close_queue(queue)
 
     def test_no_import_lock_contention(self):
         with test.support.temp_cwd():
@@ -751,6 +784,7 @@ class _TestQueue(BaseTestCase):
         # Tolerate a delta of 30 ms because of the bad clock resolution on
         # Windows (usually 15.6 ms)
         self.assertGreaterEqual(delta, 0.170)
+        close_queue(q)
 
     def test_queue_feeder_donot_stop_onexc(self):
         # bpo-30414: verify feeder handles exceptions correctly
@@ -764,7 +798,9 @@ class _TestQueue(BaseTestCase):
             q = self.Queue()
             q.put(NotSerializable())
             q.put(True)
-            self.assertTrue(q.get(timeout=0.1))
+            # bpo-30595: use a timeout of 1 second for slow buildbots
+            self.assertTrue(q.get(timeout=1.0))
+            close_queue(q)
 
 #
 #
@@ -877,10 +913,12 @@ class _TestCondition(BaseTestCase):
         p = self.Process(target=self.f, args=(cond, sleeping, woken))
         p.daemon = True
         p.start()
+        self.addCleanup(p.join)
 
         p = threading.Thread(target=self.f, args=(cond, sleeping, woken))
         p.daemon = True
         p.start()
+        self.addCleanup(p.join)
 
         # wait for both children to start sleeping
         sleeping.acquire()
@@ -923,11 +961,13 @@ class _TestCondition(BaseTestCase):
                              args=(cond, sleeping, woken, TIMEOUT1))
             p.daemon = True
             p.start()
+            self.addCleanup(p.join)
 
             t = threading.Thread(target=self.f,
                                  args=(cond, sleeping, woken, TIMEOUT1))
             t.daemon = True
             t.start()
+            self.addCleanup(t.join)
 
         # wait for them all to sleep
         for i in range(6):
@@ -946,10 +986,12 @@ class _TestCondition(BaseTestCase):
             p = self.Process(target=self.f, args=(cond, sleeping, woken))
             p.daemon = True
             p.start()
+            self.addCleanup(p.join)
 
             t = threading.Thread(target=self.f, args=(cond, sleeping, woken))
             t.daemon = True
             t.start()
+            self.addCleanup(t.join)
 
         # wait for them to all sleep
         for i in range(6):
@@ -1125,6 +1167,7 @@ class _TestEvent(BaseTestCase):
         p.daemon = True
         p.start()
         self.assertEqual(wait(), True)
+        p.join()
 
 #
 # Tests for Barrier - adapted from tests in test/lock_tests.py
@@ -1181,10 +1224,19 @@ class Bunch(object):
         self._can_exit = namespace.Event()
         if not wait_before_exit:
             self._can_exit.set()
+
+        threads = []
         for i in range(n):
             p = namespace.Process(target=self.task)
             p.daemon = True
             p.start()
+            threads.append(p)
+
+        def finalize(threads):
+            for p in threads:
+                p.join()
+
+        self._finalizer = weakref.finalize(self, finalize, threads)
 
     def task(self):
         pid = os.getpid()
@@ -1207,6 +1259,9 @@ class Bunch(object):
     def do_finish(self):
         self._can_exit.set()
 
+    def close(self):
+        self._finalizer()
+
 
 class AppendTrue(object):
     def __init__(self, obj):
@@ -1239,8 +1294,11 @@ class _TestBarrier(BaseTestCase):
 
     def run_threads(self, f, args):
         b = Bunch(self, f, args, self.N-1)
-        f(*args)
-        b.wait_for_finished()
+        try:
+            f(*args)
+            b.wait_for_finished()
+        finally:
+            b.close()
 
     @classmethod
     def multipass(cls, barrier, results, n):
@@ -1285,6 +1343,7 @@ class _TestBarrier(BaseTestCase):
         self.run_threads(self._test_wait_return_f, (self.barrier, queue))
         results = [queue.get() for i in range(self.N)]
         self.assertEqual(results.count(0), 1)
+        close_queue(queue)
 
     @classmethod
     def _test_action_f(cls, barrier, results):
@@ -1455,6 +1514,7 @@ class _TestBarrier(BaseTestCase):
             p = self.Process(target=self._test_thousand_f,
                            args=(self.barrier, passes, child_conn, lock))
             p.start()
+            self.addCleanup(p.join)
 
         for i in range(passes):
             for j in range(self.N):
@@ -2365,12 +2425,13 @@ class _TestManagerRestart(BaseTestCase):
         manager.start()
 
         p = self.Process(target=self._putter, args=(manager.address, authkey))
-        p.daemon = True
         p.start()
+        p.join()
         queue = manager.get_queue()
         self.assertEqual(queue.get(), 'hello world')
         del queue
         manager.shutdown()
+
         manager = QueueManager(
             address=addr, authkey=authkey, serializer=SERIALIZER)
         try:
@@ -2938,6 +2999,8 @@ class _TestPicklingConnections(BaseTestCase):
         w.close()
         self.assertEqual(conn.recv(), 'foobar'*2)
 
+        p.join()
+
 #
 #
 #
@@ -3263,16 +3326,16 @@ class _TestLogging(BaseTestCase):
 
         logger.setLevel(LEVEL1)
         p = self.Process(target=self._test_level, args=(writer,))
-        p.daemon = True
         p.start()
         self.assertEqual(LEVEL1, reader.recv())
+        p.join()
 
         logger.setLevel(logging.NOTSET)
         root_logger.setLevel(LEVEL2)
         p = self.Process(target=self._test_level, args=(writer,))
-        p.daemon = True
         p.start()
         self.assertEqual(LEVEL2, reader.recv())
+        p.join()
 
         root_logger.setLevel(root_level)
         logger.setLevel(level=LOG_LEVEL)
@@ -3426,7 +3489,7 @@ def _this_sub_process(q):
     except pyqueue.Empty:
         pass
 
-def _test_process(q):
+def _test_process():
     queue = multiprocessing.Queue()
     subProc = multiprocessing.Process(target=_this_sub_process, args=(queue,))
     subProc.daemon = True
@@ -3466,8 +3529,7 @@ class _file_like(object):
 class TestStdinBadfiledescriptor(unittest.TestCase):
 
     def test_queue_in_process(self):
-        queue = multiprocessing.Queue()
-        proc = multiprocessing.Process(target=_test_process, args=(queue,))
+        proc = multiprocessing.Process(target=_test_process)
         proc.start()
         proc.join()
 
@@ -4075,7 +4137,32 @@ class TestSimpleQueue(unittest.TestCase):
 # Mixins
 #
 
-class ProcessesMixin(object):
+class BaseMixin(object):
+    @classmethod
+    def setUpClass(cls):
+        cls.dangling = (multiprocessing.process._dangling.copy(),
+                        threading._dangling.copy())
+
+    @classmethod
+    def tearDownClass(cls):
+        # bpo-26762: Some multiprocessing objects like Pool create reference
+        # cycles. Trigger a garbage collection to break these cycles.
+        test.support.gc_collect()
+
+        processes = set(multiprocessing.process._dangling) - set(cls.dangling[0])
+        if processes:
+            print('Warning -- Dangling processes: %s' % processes,
+                  file=sys.stderr)
+        processes = None
+
+        threads = set(threading._dangling) - set(cls.dangling[1])
+        if threads:
+            print('Warning -- Dangling threads: %s' % threads,
+                  file=sys.stderr)
+        threads = None
+
+
+class ProcessesMixin(BaseMixin):
     TYPE = 'processes'
     Process = multiprocessing.Process
     connection = multiprocessing.connection
@@ -4098,7 +4185,7 @@ class ProcessesMixin(object):
     RawArray = staticmethod(multiprocessing.RawArray)
 
 
-class ManagerMixin(object):
+class ManagerMixin(BaseMixin):
     TYPE = 'manager'
     Process = multiprocessing.Process
     Queue = property(operator.attrgetter('manager.Queue'))
@@ -4122,6 +4209,7 @@ class ManagerMixin(object):
 
     @classmethod
     def setUpClass(cls):
+        super().setUpClass()
         cls.manager = multiprocessing.Manager()
 
     @classmethod
@@ -4129,23 +4217,35 @@ class ManagerMixin(object):
         # only the manager process should be returned by active_children()
         # but this can take a bit on slow machines, so wait a few seconds
         # if there are other children too (see #17395)
+        start_time = time.monotonic()
         t = 0.01
-        while len(multiprocessing.active_children()) > 1 and t < 5:
+        while len(multiprocessing.active_children()) > 1:
             time.sleep(t)
             t *= 2
+            dt = time.monotonic() - start_time
+            if dt >= 5.0:
+                print("Warning -- multiprocessing.Manager still has %s active "
+                      "children after %s seconds"
+                      % (multiprocessing.active_children(), dt),
+                      file=sys.stderr)
+                break
+
         gc.collect()                       # do garbage collection
         if cls.manager._number_of_objects() != 0:
             # This is not really an error since some tests do not
             # ensure that all processes which hold a reference to a
             # managed object have been joined.
-            print('Shared objects which still exist at manager shutdown:')
+            print('Warning -- Shared objects which still exist at manager '
+                  'shutdown:')
             print(cls.manager._debug_info())
         cls.manager.shutdown()
         cls.manager.join()
         cls.manager = None
 
+        super().tearDownClass()
+
 
-class ThreadsMixin(object):
+class ThreadsMixin(BaseMixin):
     TYPE = 'threads'
     Process = multiprocessing.dummy.Process
     connection = multiprocessing.dummy.connection
@@ -4222,18 +4322,33 @@ def install_tests_in_module_dict(remote_globs, start_method):
         multiprocessing.get_logger().setLevel(LOG_LEVEL)
 
     def tearDownModule():
+        need_sleep = False
+
+        # bpo-26762: Some multiprocessing objects like Pool create reference
+        # cycles. Trigger a garbage collection to break these cycles.
+        test.support.gc_collect()
+
         multiprocessing.set_start_method(old_start_method[0], force=True)
         # pause a bit so we don't get warning about dangling threads/processes
-        time.sleep(0.5)
+        processes = set(multiprocessing.process._dangling) - set(dangling[0])
+        if processes:
+            need_sleep = True
+            print('Warning -- Dangling processes: %s' % processes,
+                  file=sys.stderr)
+        processes = None
+
+        threads = set(threading._dangling) - set(dangling[1])
+        if threads:
+            need_sleep = True
+            print('Warning -- Dangling threads: %s' % threads,
+                  file=sys.stderr)
+        threads = None
+
+        # Sleep 500 ms to give time to child processes to complete.
+        if need_sleep:
+            time.sleep(0.5)
         multiprocessing.process._cleanup()
-        gc.collect()
-        tmp = set(multiprocessing.process._dangling) - set(dangling[0])
-        if tmp:
-            print('Dangling processes:', tmp, file=sys.stderr)
-        del tmp
-        tmp = set(threading._dangling) - set(dangling[1])
-        if tmp:
-            print('Dangling threads:', tmp, file=sys.stderr)
+        test.support.gc_collect()
 
     remote_globs['setUpModule'] = setUpModule
     remote_globs['tearDownModule'] = tearDownModule
diff --git a/Lib/test/bisect.py b/Lib/test/bisect.py
new file mode 100755 (executable)
index 0000000..0fd577d
--- /dev/null
@@ -0,0 +1,167 @@
+#!/usr/bin/env python3
+"""
+Command line tool to bisect failing CPython tests.
+
+Find the test_os test method which alters the environment:
+
+    ./python -m test.bisect --fail-env-changed test_os
+
+Find a reference leak in "test_os", write the list of failing tests into the
+"bisect" file:
+
+    ./python -m test.bisect -o bisect -R 3:3 test_os
+
+Load an existing list of tests from a file using -i option:
+
+    ./python -m test --list-cases -m FileTests test_os > tests
+    ./python -m test.bisect -i tests test_os
+"""
+
+import argparse
+import datetime
+import os.path
+import math
+import random
+import subprocess
+import sys
+import tempfile
+import time
+
+
+def write_tests(filename, tests):
+    with open(filename, "w") as fp:
+        for name in tests:
+            print(name, file=fp)
+        fp.flush()
+
+
+def write_output(filename, tests):
+    if not filename:
+        return
+    print("Write %s tests into %s" % (len(tests), filename))
+    write_tests(filename, tests)
+    return filename
+
+
+def format_shell_args(args):
+    return ' '.join(args)
+
+
+def list_cases(args):
+    cmd = [sys.executable, '-m', 'test', '--list-cases']
+    cmd.extend(args.test_args)
+    proc = subprocess.run(cmd,
+                          stdout=subprocess.PIPE,
+                          universal_newlines=True)
+    exitcode = proc.returncode
+    if exitcode:
+        cmd = format_shell_args(cmd)
+        print("Failed to list tests: %s failed with exit code %s"
+              % (cmd, exitcode))
+        sys.exit(exitcode)
+    tests = proc.stdout.splitlines()
+    return tests
+
+
+def run_tests(args, tests, huntrleaks=None):
+    tmp = tempfile.mktemp()
+    try:
+        write_tests(tmp, tests)
+
+        cmd = [sys.executable, '-m', 'test', '--matchfile', tmp]
+        cmd.extend(args.test_args)
+        print("+ %s" % format_shell_args(cmd))
+        proc = subprocess.run(cmd)
+        return proc.returncode
+    finally:
+        if os.path.exists(tmp):
+            os.unlink(tmp)
+
+
+def parse_args():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('-i', '--input',
+                        help='Test names produced by --list-tests written '
+                             'into a file. If not set, run --list-tests')
+    parser.add_argument('-o', '--output',
+                        help='Result of the bisection')
+    parser.add_argument('-n', '--max-tests', type=int, default=1,
+                        help='Maximum number of tests to stop the bisection '
+                             '(default: 1)')
+    parser.add_argument('-N', '--max-iter', type=int, default=100,
+                        help='Maximum number of bisection iterations '
+                             '(default: 100)')
+    # FIXME: document that following arguments are test arguments
+
+    args, test_args = parser.parse_known_args()
+    args.test_args = test_args
+    return args
+
+
+def main():
+    args = parse_args()
+
+    if args.input:
+        with open(args.input) as fp:
+            tests = [line.strip() for line in fp]
+    else:
+        tests = list_cases(args)
+
+    print("Start bisection with %s tests" % len(tests))
+    print("Test arguments: %s" % format_shell_args(args.test_args))
+    print("Bisection will stop when getting %s or less tests "
+          "(-n/--max-tests option), or after %s iterations "
+          "(-N/--max-iter option)"
+          % (args.max_tests, args.max_iter))
+    output = write_output(args.output, tests)
+    print()
+
+    start_time = time.monotonic()
+    iteration = 1
+    try:
+        while len(tests) > args.max_tests and iteration <= args.max_iter:
+            ntest = len(tests)
+            ntest = max(ntest // 2, 1)
+            subtests = random.sample(tests, ntest)
+
+            print("[+] Iteration %s: run %s tests/%s"
+                  % (iteration, len(subtests), len(tests)))
+            print()
+
+            exitcode = run_tests(args, subtests)
+
+            print("ran %s tests/%s" % (ntest, len(tests)))
+            print("exit", exitcode)
+            if exitcode:
+                print("Tests failed: use this new subtest")
+                tests = subtests
+                output = write_output(args.output, tests)
+            else:
+                print("Tests succeeded: skip this subtest, try a new subbset")
+            print()
+            iteration += 1
+    except KeyboardInterrupt:
+        print()
+        print("Bisection interrupted!")
+        print()
+
+    print("Tests (%s):" % len(tests))
+    for test in tests:
+        print("* %s" % test)
+    print()
+
+    if output:
+        print("Output written into %s" % output)
+
+    dt = math.ceil(time.monotonic() - start_time)
+    if len(tests) <= args.max_tests:
+        print("Bisection completed in %s iterations and %s"
+              % (iteration, datetime.timedelta(seconds=dt)))
+        sys.exit(1)
+    else:
+        print("Bisection failed after %s iterations and %s"
+              % (iteration, datetime.timedelta(seconds=dt)))
+
+
+if __name__ == "__main__":
+    main()
index 3387f52..0c0e0d3 100644 (file)
@@ -1,2 +1,3 @@
+# Also used by idlelib.test_idle.test_config.
 [Foo Bar]
 foo=newbar
index bccd97a..b25e6c1 100644 (file)
@@ -61,8 +61,9 @@ class TestModule(unittest.TestCase):
         self.assertEqual(datetime.MAXYEAR, 9999)
 
     def test_name_cleanup(self):
-        if '_Fast' not in str(self):
-            return
+        if '_Pure' in self.__class__.__name__:
+            self.skipTest('Only run for Fast C implementation')
+
         datetime = datetime_module
         names = set(name for name in dir(datetime)
                     if not name.startswith('__') and not name.endswith('__'))
@@ -72,8 +73,9 @@ class TestModule(unittest.TestCase):
         self.assertEqual(names - allowed, set([]))
 
     def test_divide_and_round(self):
-        if '_Fast' in str(self):
-            return
+        if '_Fast' in self.__class__.__name__:
+            self.skipTest('Only run for Pure Python implementation')
+
         dar = datetime_module._divide_and_round
 
         self.assertEqual(dar(-10, -3), 3)
@@ -2851,7 +2853,7 @@ class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
         self.assertRaises(TypeError, t.strftime, "%Z")
 
         # Issue #6697:
-        if '_Fast' in str(self):
+        if '_Fast' in self.__class__.__name__:
             Badtzname.tz = '\ud800'
             self.assertRaises(ValueError, t.strftime, "%Z")
 
index 713039d..6af79ad 100644 (file)
@@ -11,7 +11,8 @@ active threads survive in the child after a fork(); this is an error.
 
 import os, sys, time, unittest
 import test.support as support
-_thread = support.import_module('_thread')
+
+threading = support.import_module('threading')
 
 LONGSLEEP = 2
 SHORTSLEEP = 0.5
@@ -20,8 +21,19 @@ NUM_THREADS = 4
 class ForkWait(unittest.TestCase):
 
     def setUp(self):
+        self._threading_key = support.threading_setup()
         self.alive = {}
         self.stop = 0
+        self.threads = []
+
+    def tearDown(self):
+        # Stop threads
+        self.stop = 1
+        for thread in self.threads:
+            thread.join()
+        thread = None
+        self.threads.clear()
+        support.threading_cleanup(*self._threading_key)
 
     def f(self, id):
         while not self.stop:
@@ -43,10 +55,11 @@ class ForkWait(unittest.TestCase):
         self.assertEqual(spid, cpid)
         self.assertEqual(status, 0, "cause = %d, exit = %d" % (status&0xff, status>>8))
 
-    @support.reap_threads
     def test_wait(self):
         for i in range(NUM_THREADS):
-            _thread.start_new(self.f, (i,))
+            thread = threading.Thread(target=self.f, args=(i,))
+            thread.start()
+            self.threads.append(thread)
 
         # busy-loop to wait for threads
         deadline = time.monotonic() + 10.0
@@ -75,8 +88,4 @@ class ForkWait(unittest.TestCase):
             os._exit(n)
         else:
             # Parent
-            try:
-                self.wait_impl(cpid)
-            finally:
-                # Tell threads to die
-                self.stop = 1
+            self.wait_impl(cpid)
index 7ba0e6e..3427b51 100644 (file)
@@ -1,5 +1,5 @@
 # We import importlib *ASAP* in order to test #15386
 import importlib
 
-from test.libregrtest.cmdline import _parse_args, RESOURCE_NAMES
+from test.libregrtest.cmdline import _parse_args, RESOURCE_NAMES, ALL_RESOURCES
 from test.libregrtest.main import main
index bf64062..4999fa7 100644 (file)
@@ -127,8 +127,17 @@ Pattern examples:
 """
 
 
-RESOURCE_NAMES = ('audio', 'curses', 'largefile', 'network',
-                  'decimal', 'cpu', 'subprocess', 'urlfetch', 'gui', 'tzdata')
+ALL_RESOURCES = ('audio', 'curses', 'largefile', 'network',
+                 'decimal', 'cpu', 'subprocess', 'urlfetch', 'gui')
+
+# Other resources excluded from --use=all:
+#
+# - extralagefile (ex: test_zipfile64): really too slow to be enabled
+#   "by default"
+# - tzdata: while needed to validate fully test_datetime, it makes
+#   test_datetime too slow (15-20 min on some buildbots) and so is disabled by
+#   default (see bpo-30822).
+RESOURCE_NAMES = ALL_RESOURCES + ('extralargefile', 'tzdata')
 
 class _ArgParser(argparse.ArgumentParser):
 
@@ -255,6 +264,9 @@ def _create_parser():
                             ' , don\'t execute them')
     group.add_argument('-P', '--pgo', dest='pgo', action='store_true',
                        help='enable Profile Guided Optimization training')
+    group.add_argument('--fail-env-changed', action='store_true',
+                       help='if a test file alters the environment, mark '
+                            'the test as failed')
 
     return parser
 
@@ -341,7 +353,7 @@ def _parse_args(args, **kwargs):
         for a in ns.use:
             for r in a:
                 if r == 'all':
-                    ns.use_resources[:] = RESOURCE_NAMES
+                    ns.use_resources[:] = ALL_RESOURCES
                     continue
                 if r == 'none':
                     del ns.use_resources[:]
index 527de17..d44f69d 100644 (file)
@@ -256,9 +256,13 @@ class Regrtest:
             if isinstance(test, unittest.TestSuite):
                 self._list_cases(test)
             elif isinstance(test, unittest.TestCase):
-                print(test.id())
+                if support._match_test(test):
+                    print(test.id())
 
     def list_cases(self):
+        support.verbose = False
+        support.match_tests = self.ns.match_tests
+
         for test in self.selected:
             abstest = get_abs_module(self.ns, test)
             try:
@@ -411,14 +415,14 @@ class Regrtest:
                 yield test
                 if self.bad:
                     return
+                if self.ns.fail_env_changed and self.environment_changed:
+                    return
 
     def display_header(self):
         # Print basic platform information
         print("==", platform.python_implementation(), *sys.version.split())
         print("==", platform.platform(aliased=True),
                       "%s-endian" % sys.byteorder)
-        print("== hash algorithm:", sys.hash_info.algorithm,
-              "64bit" if sys.maxsize > 2**32 else "32bit")
         print("== cwd:", os.getcwd())
         cpu_count = os.cpu_count()
         if cpu_count:
@@ -426,7 +430,6 @@ class Regrtest:
         print("== encodings: locale=%s, FS=%s"
               % (locale.getpreferredencoding(False),
                  sys.getfilesystemencoding()))
-        print("Testing with flags:", sys.flags)
 
     def run_tests(self):
         # For a partial run, we do not need to clutter the output.
@@ -474,6 +477,8 @@ class Regrtest:
             result = "FAILURE"
         elif self.interrupted:
             result = "INTERRUPTED"
+        elif self.ns.fail_env_changed and self.environment_changed:
+            result = "ENV CHANGED"
         else:
             result = "SUCCESS"
         print("Tests result: %s" % result)
@@ -534,7 +539,13 @@ class Regrtest:
             self.rerun_failed_tests()
 
         self.finalize()
-        sys.exit(len(self.bad) > 0 or self.interrupted)
+        if self.bad:
+            sys.exit(2)
+        if self.interrupted:
+            sys.exit(130)
+        if self.ns.fail_env_changed and self.environment_changed:
+            sys.exit(3)
+        sys.exit(0)
 
 
 def removepy(names):
index c69fc0f..0bd8288 100644 (file)
@@ -68,6 +68,14 @@ def dash_R(the_module, test, indirect_test, huntrleaks):
         for obj in abc.__subclasses__() + [abc]:
             abcs[obj] = obj._abc_registry.copy()
 
+    # bpo-31217: Integer pool to get a single integer object for the same
+    # value. The pool is used to prevent false alarm when checking for memory
+    # block leaks. Fill the pool with values in -1000..1000 which are the most
+    # common (reference, memory block, file descriptor) differences.
+    int_pool = {value: value for value in range(-1000, 1000)}
+    def get_pooled_int(value):
+        return int_pool.setdefault(value, value)
+
     nwarmup, ntracked, fname = huntrleaks
     fname = os.path.join(support.SAVEDCWD, fname)
     repcount = nwarmup + ntracked
@@ -86,32 +94,44 @@ def dash_R(the_module, test, indirect_test, huntrleaks):
                                                          abcs)
         print('.', end='', file=sys.stderr, flush=True)
         if i >= nwarmup:
-            rc_deltas[i] = rc_after - rc_before
-            alloc_deltas[i] = alloc_after - alloc_before
-            fd_deltas[i] = fd_after - fd_before
+            rc_deltas[i] = get_pooled_int(rc_after - rc_before)
+            alloc_deltas[i] = get_pooled_int(alloc_after - alloc_before)
+            fd_deltas[i] = get_pooled_int(fd_after - fd_before)
         alloc_before = alloc_after
         rc_before = rc_after
         fd_before = fd_after
     print(file=sys.stderr)
+
     # These checkers return False on success, True on failure
     def check_rc_deltas(deltas):
+        # Checker for reference counters and memomry blocks.
+        #
+        # bpo-30776: Try to ignore false positives:
+        #
+        #   [3, 0, 0]
+        #   [0, 1, 0]
+        #   [8, -8, 1]
+        #
+        # Expected leaks:
+        #
+        #   [5, 5, 6]
+        #   [10, 1, 1]
+        return all(delta >= 1 for delta in deltas)
+
+    def check_fd_deltas(deltas):
         return any(deltas)
-    def check_alloc_deltas(deltas):
-        # At least 1/3rd of 0s
-        if 3 * deltas.count(0) < len(deltas):
-            return True
-        # Nothing else than 1s, 0s and -1s
-        if not set(deltas) <= {1,0,-1}:
-            return True
-        return False
+
     failed = False
     for deltas, item_name, checker in [
         (rc_deltas, 'references', check_rc_deltas),
-        (alloc_deltas, 'memory blocks', check_alloc_deltas),
-        (fd_deltas, 'file descriptors', check_rc_deltas)]:
+        (alloc_deltas, 'memory blocks', check_rc_deltas),
+        (fd_deltas, 'file descriptors', check_fd_deltas)
+    ]:
+        # ignore warmup runs
+        deltas = deltas[nwarmup:]
         if checker(deltas):
             msg = '%s leaked %s %s, sum=%s' % (
-                test, deltas[nwarmup:], item_name, sum(deltas))
+                test, deltas, item_name, sum(deltas))
             print(msg, file=sys.stderr, flush=True)
             with open(fname, "a") as refrep:
                 print(msg, file=refrep)
index fda4ca1..d716b83 100644 (file)
@@ -143,6 +143,10 @@ def runtest(ns, test):
 runtest.stringio = None
 
 
+def post_test_cleanup():
+    support.reap_children()
+
+
 def runtest_inner(ns, test, display_failure=True):
     support.unload(test)
 
@@ -170,6 +174,7 @@ def runtest_inner(ns, test, display_failure=True):
             if ns.huntrleaks:
                 refleak = dash_R(the_module, test, test_runner, ns.huntrleaks)
             test_time = time.time() - start_time
+        post_test_cleanup()
     except support.ResourceDenied as msg:
         if not ns.quiet and not ns.pgo:
             print(test, "skipped --", msg, flush=True)
index a6cb3b1..4fa154f 100644 (file)
@@ -461,21 +461,28 @@ class ConditionTests(BaseTestCase):
         # construct.  In particular, it is possible that this can no longer
         # be conveniently guaranteed should their implementation ever change.
         N = 5
+        ready = []
         results1 = []
         results2 = []
         phase_num = 0
         def f():
             cond.acquire()
+            ready.append(phase_num)
             result = cond.wait()
             cond.release()
             results1.append((result, phase_num))
             cond.acquire()
+            ready.append(phase_num)
             result = cond.wait()
             cond.release()
             results2.append((result, phase_num))
         b = Bunch(f, N)
         b.wait_for_started()
-        _wait()
+        # first wait, to ensure all workers settle into cond.wait() before
+        # we continue. See issues #8799 and #30727.
+        while len(ready) < 5:
+            _wait()
+        ready.clear()
         self.assertEqual(results1, [])
         # Notify 3 threads at first
         cond.acquire()
@@ -487,9 +494,9 @@ class ConditionTests(BaseTestCase):
             _wait()
         self.assertEqual(results1, [(True, 1)] * 3)
         self.assertEqual(results2, [])
-        # first wait, to ensure all workers settle into cond.wait() before
-        # we continue. See issue #8799
-        _wait()
+        # make sure all awaken workers settle into cond.wait()
+        while len(ready) < 3:
+            _wait()
         # Notify 5 threads: they might be in their first or second wait
         cond.acquire()
         cond.notify(5)
@@ -500,7 +507,9 @@ class ConditionTests(BaseTestCase):
             _wait()
         self.assertEqual(results1, [(True, 1)] * 3 + [(True, 2)] * 2)
         self.assertEqual(results2, [(True, 2)] * 3)
-        _wait() # make sure all workers settle into cond.wait()
+        # make sure all workers settle into cond.wait()
+        while len(ready) < 5:
+            _wait()
         # Notify all threads: they are all in their second wait
         cond.acquire()
         cond.notify_all()
index d9a60b4..6d35c58 100644 (file)
@@ -1,14 +1,53 @@
 """Module for testing the behavior of generics across different modules."""
 
-from typing import TypeVar, Generic
+import sys
+from textwrap import dedent
+from typing import TypeVar, Generic, Optional
 
-T = TypeVar('T')
 
+if sys.version_info[:2] >= (3, 6):
+    exec(dedent("""
+    default_a: Optional['A'] = None
+    default_b: Optional['B'] = None
 
-class A(Generic[T]):
-    pass
+    T = TypeVar('T')
 
 
-class B(Generic[T]):
     class A(Generic[T]):
-        pass
+        some_b: 'B'
+
+
+    class B(Generic[T]):
+        class A(Generic[T]):
+            pass
+
+        my_inner_a1: 'B.A'
+        my_inner_a2: A
+        my_outer_a: 'A'  # unless somebody calls get_type_hints with localns=B.__dict__
+    """))
+else:  # This should stay in sync with the syntax above.
+    __annotations__ = dict(
+        default_a=Optional['A'],
+        default_b=Optional['B'],
+    )
+    default_a = None
+    default_b = None
+
+    T = TypeVar('T')
+
+
+    class A(Generic[T]):
+        __annotations__ = dict(
+            some_b='B'
+        )
+
+
+    class B(Generic[T]):
+        class A(Generic[T]):
+            pass
+
+        __annotations__ = dict(
+            my_inner_a1='B.A',
+            my_inner_a2=A,
+            my_outer_a='A'  # unless somebody calls get_type_hints with localns=B.__dict__
+        )
diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py
new file mode 100644 (file)
index 0000000..15cce34
--- /dev/null
@@ -0,0 +1,471 @@
+"""
+Collect various informations about Python to help debugging test failures.
+"""
+from __future__ import print_function
+import re
+import sys
+import traceback
+
+
+def normalize_text(text):
+    if text is None:
+        return None
+    text = str(text)
+    text = re.sub(r'\s+', ' ', text)
+    return text.strip()
+
+
+class PythonInfo:
+    def __init__(self):
+        self.info = {}
+
+    def add(self, key, value):
+        if key in self.info:
+            raise ValueError("duplicate key: %r" % key)
+
+        if value is None:
+            return
+
+        if not isinstance(value, int):
+            if not isinstance(value, str):
+                # convert other objects like sys.flags to string
+                value = str(value)
+
+            value = value.strip()
+            if not value:
+                return
+
+        self.info[key] = value
+
+    def get_infos(self):
+        """
+        Get informations as a key:value dictionary where values are strings.
+        """
+        return {key: str(value) for key, value in self.info.items()}
+
+
+def copy_attributes(info_add, obj, name_fmt, attributes, *, formatter=None):
+    for attr in attributes:
+        value = getattr(obj, attr, None)
+        if value is None:
+            continue
+        name = name_fmt % attr
+        if formatter is not None:
+            value = formatter(attr, value)
+        info_add(name, value)
+
+
+def call_func(info_add, name, mod, func_name, *, formatter=None):
+    try:
+        func = getattr(mod, func_name)
+    except AttributeError:
+        return
+    value = func()
+    if formatter is not None:
+        value = formatter(value)
+    info_add(name, value)
+
+
+def collect_sys(info_add):
+    attributes = (
+        '_framework',
+        'abiflags',
+        'api_version',
+        'builtin_module_names',
+        'byteorder',
+        'dont_write_bytecode',
+        'executable',
+        'flags',
+        'float_info',
+        'float_repr_style',
+        'hash_info',
+        'hexversion',
+        'implementation',
+        'int_info',
+        'maxsize',
+        'maxunicode',
+        'path',
+        'platform',
+        'prefix',
+        'thread_info',
+        'version',
+        'version_info',
+        'winver',
+    )
+    copy_attributes(info_add, sys, 'sys.%s', attributes)
+
+    call_func(info_add, 'sys.androidapilevel', sys, 'getandroidapilevel')
+    call_func(info_add, 'sys.windowsversion', sys, 'getwindowsversion')
+
+    encoding = sys.getfilesystemencoding()
+    if hasattr(sys, 'getfilesystemencodeerrors'):
+        encoding = '%s/%s' % (encoding, sys.getfilesystemencodeerrors())
+    info_add('sys.filesystem_encoding', encoding)
+
+    for name in ('stdin', 'stdout', 'stderr'):
+        stream = getattr(sys, name)
+        if stream is None:
+            continue
+        encoding = getattr(stream, 'encoding', None)
+        if not encoding:
+            continue
+        errors = getattr(stream, 'errors', None)
+        if errors:
+            encoding = '%s/%s' % (encoding, errors)
+        info_add('sys.%s.encoding' % name, encoding)
+
+
+def collect_platform(info_add):
+    import platform
+
+    arch = platform.architecture()
+    arch = ' '.join(filter(bool, arch))
+    info_add('platform.architecture', arch)
+
+    info_add('platform.python_implementation',
+             platform.python_implementation())
+    info_add('platform.platform',
+             platform.platform(aliased=True))
+
+
+def collect_locale(info_add):
+    import locale
+
+    info_add('locale.encoding', locale.getpreferredencoding(False))
+
+
+def collect_os(info_add):
+    import os
+
+    def format_attr(attr, value):
+        if attr in ('supports_follow_symlinks', 'supports_fd',
+                    'supports_effective_ids'):
+            return str(sorted(func.__name__ for func in value))
+        else:
+            return value
+
+    attributes = (
+        'name',
+        'supports_bytes_environ',
+        'supports_effective_ids',
+        'supports_fd',
+        'supports_follow_symlinks',
+    )
+    copy_attributes(info_add, os, 'os.%s', attributes, formatter=format_attr)
+
+    info_add("os.cwd", os.getcwd())
+
+    call_func(info_add, 'os.uid', os, 'getuid')
+    call_func(info_add, 'os.gid', os, 'getgid')
+    call_func(info_add, 'os.uname', os, 'uname')
+
+    if hasattr(os, 'getgroups'):
+        groups = os.getgroups()
+        groups = map(str, groups)
+        groups = ', '.join(groups)
+        info_add("os.groups", groups)
+
+    if hasattr(os, 'getlogin'):
+        try:
+            login = os.getlogin()
+        except OSError:
+            # getlogin() fails with "OSError: [Errno 25] Inappropriate ioctl
+            # for device" on Travis CI
+            pass
+        else:
+            info_add("os.login", login)
+
+    if hasattr(os, 'cpu_count'):
+        cpu_count = os.cpu_count()
+        if cpu_count:
+            info_add('os.cpu_count', cpu_count)
+
+    call_func(info_add, 'os.loadavg', os, 'getloadavg')
+
+    # Get environment variables: filter to list
+    # to not leak sensitive information
+    ENV_VARS = (
+        "CC",
+        "COMSPEC",
+        "DISPLAY",
+        "DISTUTILS_USE_SDK",
+        "DYLD_LIBRARY_PATH",
+        "HOME",
+        "HOMEDRIVE",
+        "HOMEPATH",
+        "LANG",
+        "LD_LIBRARY_PATH",
+        "MACOSX_DEPLOYMENT_TARGET",
+        "MAKEFLAGS",
+        "MSSDK",
+        "PATH",
+        "SDK_TOOLS_BIN",
+        "SHELL",
+        "TEMP",
+        "TERM",
+        "TMP",
+        "TMPDIR",
+        "USERPROFILE",
+        "WAYLAND_DISPLAY",
+    )
+    for name, value in os.environ.items():
+        uname = name.upper()
+        if (uname in ENV_VARS or uname.startswith(("PYTHON", "LC_"))
+           # Visual Studio: VS140COMNTOOLS
+           or (uname.startswith("VS") and uname.endswith("COMNTOOLS"))):
+            info_add('os.environ[%s]' % name, value)
+
+    if hasattr(os, 'umask'):
+        mask = os.umask(0)
+        os.umask(mask)
+        info_add("os.umask", '%03o' % mask)
+
+    if hasattr(os, 'getrandom'):
+        # PEP 524: Check if system urandom is initialized
+        try:
+            os.getrandom(1, os.GRND_NONBLOCK)
+            state = 'ready (initialized)'
+        except BlockingIOError as exc:
+            state = 'not seeded yet (%s)' % exc
+        info_add('os.getrandom', state)
+
+
+def collect_readline(info_add):
+    try:
+        import readline
+    except ImportError:
+        return
+
+    def format_attr(attr, value):
+        if isinstance(value, int):
+            return "%#x" % value
+        else:
+            return value
+
+    attributes = (
+        "_READLINE_VERSION",
+        "_READLINE_RUNTIME_VERSION",
+        "_READLINE_LIBRARY_VERSION",
+    )
+    copy_attributes(info_add, readline, 'readline.%s', attributes,
+                    formatter=format_attr)
+
+
+def collect_gdb(info_add):
+    import subprocess
+
+    try:
+        proc = subprocess.Popen(["gdb", "-nx", "--version"],
+                                stdout=subprocess.PIPE,
+                                stderr=subprocess.PIPE,
+                                universal_newlines=True)
+        version = proc.communicate()[0]
+    except OSError:
+        return
+
+    # Only keep the first line
+    version = version.splitlines()[0]
+    info_add('gdb_version', version)
+
+
+def collect_tkinter(info_add):
+    try:
+        import _tkinter
+    except ImportError:
+        pass
+    else:
+        attributes = ('TK_VERSION', 'TCL_VERSION')
+        copy_attributes(info_add, _tkinter, 'tkinter.%s', attributes)
+
+    try:
+        import tkinter
+    except ImportError:
+        pass
+    else:
+        tcl = tkinter.Tcl()
+        patchlevel = tcl.call('info', 'patchlevel')
+        info_add('tkinter.info_patchlevel', patchlevel)
+
+
+def collect_time(info_add):
+    import time
+
+    attributes = (
+        'altzone',
+        'daylight',
+        'timezone',
+        'tzname',
+    )
+    copy_attributes(info_add, time, 'time.%s', attributes)
+
+    if not hasattr(time, 'get_clock_info'):
+        return
+
+    for clock in ('time', 'perf_counter'):
+        tinfo = time.get_clock_info(clock)
+        info_add('time.%s' % clock, tinfo)
+
+
+def collect_sysconfig(info_add):
+    import sysconfig
+
+    for name in (
+        'ABIFLAGS',
+        'ANDROID_API_LEVEL',
+        'CC',
+        'CCSHARED',
+        'CFLAGS',
+        'CFLAGSFORSHARED',
+        'PY_LDFLAGS',
+        'CONFIG_ARGS',
+        'HOST_GNU_TYPE',
+        'MACHDEP',
+        'MULTIARCH',
+        'OPT',
+        'PY_CFLAGS',
+        'PY_CFLAGS_NODIST',
+        'Py_DEBUG',
+        'Py_ENABLE_SHARED',
+        'SHELL',
+        'SOABI',
+        'prefix',
+    ):
+        value = sysconfig.get_config_var(name)
+        if name == 'ANDROID_API_LEVEL' and not value:
+            # skip ANDROID_API_LEVEL=0
+            continue
+        value = normalize_text(value)
+        info_add('sysconfig[%s]' % name, value)
+
+
+def collect_ssl(info_add):
+    try:
+        import ssl
+    except ImportError:
+        return
+
+    def format_attr(attr, value):
+        if attr.startswith('OP_'):
+            return '%#8x' % value
+        else:
+            return value
+
+    attributes = (
+        'OPENSSL_VERSION',
+        'OPENSSL_VERSION_INFO',
+        'HAS_SNI',
+        'OP_ALL',
+        'OP_NO_TLSv1_1',
+    )
+    copy_attributes(info_add, ssl, 'ssl.%s', attributes, formatter=format_attr)
+
+
+def collect_socket(info_add):
+    import socket
+
+    hostname = socket.gethostname()
+    info_add('socket.hostname', hostname)
+
+
+def collect_sqlite(info_add):
+    try:
+        import sqlite3
+    except ImportError:
+        return
+
+    attributes = ('version', 'sqlite_version')
+    copy_attributes(info_add, sqlite3, 'sqlite3.%s', attributes)
+
+
+def collect_zlib(info_add):
+    try:
+        import zlib
+    except ImportError:
+        return
+
+    attributes = ('ZLIB_VERSION', 'ZLIB_RUNTIME_VERSION')
+    copy_attributes(info_add, zlib, 'zlib.%s', attributes)
+
+
+def collect_expat(info_add):
+    try:
+        from xml.parsers import expat
+    except ImportError:
+        return
+
+    attributes = ('EXPAT_VERSION',)
+    copy_attributes(info_add, expat, 'expat.%s', attributes)
+
+
+def collect_decimal(info_add):
+    try:
+        import _decimal
+    except ImportError:
+        return
+
+    attributes = ('__libmpdec_version__',)
+    copy_attributes(info_add, _decimal, '_decimal.%s', attributes)
+
+
+def collect_info(info):
+    error = False
+    info_add = info.add
+
+    for collect_func in (
+        # collect_os() should be the first, to check the getrandom() status
+        collect_os,
+
+        collect_gdb,
+        collect_locale,
+        collect_platform,
+        collect_readline,
+        collect_socket,
+        collect_sqlite,
+        collect_ssl,
+        collect_sys,
+        collect_sysconfig,
+        collect_time,
+        collect_tkinter,
+        collect_zlib,
+        collect_expat,
+        collect_decimal,
+    ):
+        try:
+            collect_func(info_add)
+        except Exception as exc:
+            error = True
+            print("ERROR: %s() failed" % (collect_func.__name__),
+                  file=sys.stderr)
+            traceback.print_exc(file=sys.stderr)
+            print(file=sys.stderr)
+            sys.stderr.flush()
+
+    return error
+
+
+def dump_info(info, file=None):
+    title = "Python debug information"
+    print(title)
+    print("=" * len(title))
+    print()
+
+    infos = info.get_infos()
+    infos = sorted(infos.items())
+    for key, value in infos:
+        value = value.replace("\n", " ")
+        print("%s: %s" % (key, value))
+    print()
+
+
+def main():
+    info = PythonInfo()
+    error = collect_info(info)
+    dump_info(info)
+
+    if error:
+        print("Collection failed: exit with error", file=sys.stderr)
+        sys.exit(1)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/Lib/test/sha256.pem b/Lib/test/sha256.pem
deleted file mode 100644 (file)
index d3db4b8..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-# Certificate chain for https://sha256.tbs-internet.com
- 0 s:/C=FR/postalCode=14000/ST=Calvados/L=CAEN/street=22 rue de Bretagne/O=TBS INTERNET/OU=0002 440443810/OU=sha-256 production/CN=sha256.tbs-internet.com
-   i:/C=FR/ST=Calvados/L=Caen/O=TBS INTERNET/OU=Terms and Conditions: http://www.tbs-internet.com/CA/repository/OU=TBS INTERNET CA/CN=TBS X509 CA SGC
------BEGIN CERTIFICATE-----
-MIIGXDCCBUSgAwIBAgIRAKpVmHgg9nfCodAVwcP4siwwDQYJKoZIhvcNAQELBQAw
-gcQxCzAJBgNVBAYTAkZSMREwDwYDVQQIEwhDYWx2YWRvczENMAsGA1UEBxMEQ2Fl
-bjEVMBMGA1UEChMMVEJTIElOVEVSTkVUMUgwRgYDVQQLEz9UZXJtcyBhbmQgQ29u
-ZGl0aW9uczogaHR0cDovL3d3dy50YnMtaW50ZXJuZXQuY29tL0NBL3JlcG9zaXRv
-cnkxGDAWBgNVBAsTD1RCUyBJTlRFUk5FVCBDQTEYMBYGA1UEAxMPVEJTIFg1MDkg
-Q0EgU0dDMB4XDTEyMDEwNDAwMDAwMFoXDTE0MDIxNzIzNTk1OVowgcsxCzAJBgNV
-BAYTAkZSMQ4wDAYDVQQREwUxNDAwMDERMA8GA1UECBMIQ2FsdmFkb3MxDTALBgNV
-BAcTBENBRU4xGzAZBgNVBAkTEjIyIHJ1ZSBkZSBCcmV0YWduZTEVMBMGA1UEChMM
-VEJTIElOVEVSTkVUMRcwFQYDVQQLEw4wMDAyIDQ0MDQ0MzgxMDEbMBkGA1UECxMS
-c2hhLTI1NiBwcm9kdWN0aW9uMSAwHgYDVQQDExdzaGEyNTYudGJzLWludGVybmV0
-LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKQIX/zdJcyxty0m
-PM1XQSoSSifueS3AVcgqMsaIKS/u+rYzsv4hQ/qA6vLn5m5/ewUcZDj7zdi6rBVf
-PaVNXJ6YinLX0tkaW8TEjeVuZG5yksGZlhCt1CJ1Ho9XLiLaP4uJ7MCoNUntpJ+E
-LfrOdgsIj91kPmwjDJeztVcQCvKzhjVJA/KxdInc0JvOATn7rpaSmQI5bvIjufgo
-qVsTPwVFzuUYULXBk7KxRT7MiEqnd5HvviNh0285QC478zl3v0I0Fb5El4yD3p49
-IthcRnxzMKc0UhU5ogi0SbONyBfm/mzONVfSxpM+MlyvZmJqrbuuLoEDzJD+t8PU
-xSuzgbcCAwEAAaOCAj4wggI6MB8GA1UdIwQYMBaAFAdEdoWTKLx/bXjSCuv6TEvf
-2YIfMB0GA1UdDgQWBBT/qTGYdaj+f61c2IRFL/B1eEsM8DAOBgNVHQ8BAf8EBAMC
-BaAwDAYDVR0TAQH/BAIwADA0BgNVHSUELTArBggrBgEFBQcDAQYIKwYBBQUHAwIG
-CisGAQQBgjcKAwMGCWCGSAGG+EIEATBLBgNVHSAERDBCMEAGCisGAQQB5TcCBAEw
-MjAwBggrBgEFBQcCARYkaHR0cHM6Ly93d3cudGJzLWludGVybmV0LmNvbS9DQS9D
-UFM0MG0GA1UdHwRmMGQwMqAwoC6GLGh0dHA6Ly9jcmwudGJzLWludGVybmV0LmNv
-bS9UQlNYNTA5Q0FTR0MuY3JsMC6gLKAqhihodHRwOi8vY3JsLnRicy14NTA5LmNv
-bS9UQlNYNTA5Q0FTR0MuY3JsMIGmBggrBgEFBQcBAQSBmTCBljA4BggrBgEFBQcw
-AoYsaHR0cDovL2NydC50YnMtaW50ZXJuZXQuY29tL1RCU1g1MDlDQVNHQy5jcnQw
-NAYIKwYBBQUHMAKGKGh0dHA6Ly9jcnQudGJzLXg1MDkuY29tL1RCU1g1MDlDQVNH
-Qy5jcnQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLnRicy14NTA5LmNvbTA/BgNV
-HREEODA2ghdzaGEyNTYudGJzLWludGVybmV0LmNvbYIbd3d3LnNoYTI1Ni50YnMt
-aW50ZXJuZXQuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQA0pOuL8QvAa5yksTbGShzX
-ABApagunUGoEydv4YJT1MXy9tTp7DrWaozZSlsqBxrYAXP1d9r2fuKbEniYHxaQ0
-UYaf1VSIlDo1yuC8wE7wxbHDIpQ/E5KAyxiaJ8obtDhFstWAPAH+UoGXq0kj2teN
-21sFQ5dXgA95nldvVFsFhrRUNB6xXAcaj0VZFhttI0ZfQZmQwEI/P+N9Jr40OGun
-aa+Dn0TMeUH4U20YntfLbu2nDcJcYfyurm+8/0Tr4HznLnedXu9pCPYj0TaddrgT
-XO0oFiyy7qGaY6+qKh71yD64Y3ycCJ/HR9Wm39mjZYc9ezYwT4noP6r7Lk8YO7/q
------END CERTIFICATE-----
- 1 s:/C=FR/ST=Calvados/L=Caen/O=TBS INTERNET/OU=Terms and Conditions: http://www.tbs-internet.com/CA/repository/OU=TBS INTERNET CA/CN=TBS X509 CA SGC
-   i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
------BEGIN CERTIFICATE-----
-MIIFVjCCBD6gAwIBAgIQXpDZ0ETJMV02WTx3GTnhhTANBgkqhkiG9w0BAQUFADBv
-MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
-ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
-eHRlcm5hbCBDQSBSb290MB4XDTA1MTIwMTAwMDAwMFoXDTE5MDYyNDE5MDYzMFow
-gcQxCzAJBgNVBAYTAkZSMREwDwYDVQQIEwhDYWx2YWRvczENMAsGA1UEBxMEQ2Fl
-bjEVMBMGA1UEChMMVEJTIElOVEVSTkVUMUgwRgYDVQQLEz9UZXJtcyBhbmQgQ29u
-ZGl0aW9uczogaHR0cDovL3d3dy50YnMtaW50ZXJuZXQuY29tL0NBL3JlcG9zaXRv
-cnkxGDAWBgNVBAsTD1RCUyBJTlRFUk5FVCBDQTEYMBYGA1UEAxMPVEJTIFg1MDkg
-Q0EgU0dDMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsgOkO3f7wzN6
-rOjg45tR5vjBfzK7qmV9IBxb/QW9EEXxG+E7FNhZqQLtwGBKoSsHTnQqV75wWMk0
-9tinWvftBkSpj5sTi/8cbzJfUvTSVYh3Qxv6AVVjMMH/ruLjE6y+4PoaPs8WoYAQ
-ts5R4Z1g8c/WnTepLst2x0/Wv7GmuoQi+gXvHU6YrBiu7XkeYhzc95QdviWSJRDk
-owhb5K43qhcvjRmBfO/paGlCliDGZp8mHwrI21mwobWpVjTxZRwYO3bd4+TGcI4G
-Ie5wmHwE8F7SK1tgSqbBacKjDa93j7txKkfz/Yd2n7TGqOXiHPsJpG655vrKtnXk
-9vs1zoDeJQIDAQABo4IBljCCAZIwHQYDVR0OBBYEFAdEdoWTKLx/bXjSCuv6TEvf
-2YIfMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMCAGA1UdJQQZ
-MBcGCisGAQQBgjcKAwMGCWCGSAGG+EIEATAYBgNVHSAEETAPMA0GCysGAQQBgOU3
-AgQBMHsGA1UdHwR0MHIwOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0Fk
-ZFRydXN0RXh0ZXJuYWxDQVJvb3QuY3JsMDagNKAyhjBodHRwOi8vY3JsLmNvbW9k
-by5uZXQvQWRkVHJ1c3RFeHRlcm5hbENBUm9vdC5jcmwwgYAGCCsGAQUFBwEBBHQw
-cjA4BggrBgEFBQcwAoYsaHR0cDovL2NydC5jb21vZG9jYS5jb20vQWRkVHJ1c3RV
-VE5TR0NDQS5jcnQwNgYIKwYBBQUHMAKGKmh0dHA6Ly9jcnQuY29tb2RvLm5ldC9B
-ZGRUcnVzdFVUTlNHQ0NBLmNydDARBglghkgBhvhCAQEEBAMCAgQwDQYJKoZIhvcN
-AQEFBQADggEBAK2zEzs+jcIrVK9oDkdDZNvhuBYTdCfpxfFs+OAujW0bIfJAy232
-euVsnJm6u/+OrqKudD2tad2BbejLLXhMZViaCmK7D9nrXHx4te5EP8rL19SUVqLY
-1pTnv5dhNgEgvA7n5lIzDSYs7yRLsr7HJsYPr6SeYSuZizyX1SNz7ooJ32/F3X98
-RB0Mlc/E0OyOrkQ9/y5IrnpnaSora8CnUrV5XNOg+kyCz9edCyx4D5wXYcwZPVWz
-8aDqquESrezPyjtfi4WRO4s/VD3HLZvOxzMrWAVYCDG9FxaOhF0QGuuG1F7F3GKV
-v6prNyCl016kRl2j1UT+a7gLd8fA25A4C9E=
------END CERTIFICATE-----
- 2 s:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
-   i:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN - DATACorp SGC
------BEGIN CERTIFICATE-----
-MIIEZjCCA06gAwIBAgIQUSYKkxzif5zDpV954HKugjANBgkqhkiG9w0BAQUFADCB
-kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
-Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
-dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw
-IFNHQzAeFw0wNTA2MDcwODA5MTBaFw0xOTA2MjQxOTA2MzBaMG8xCzAJBgNVBAYT
-AlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0
-ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB
-IFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC39xoz5vIABC05
-4E5b7R+8bA/Ntfojts7emxEzl6QpTH2Tn71KvJPtAxrjj8/lbVBa1pcplFqAsEl6
-2y6V/bjKvzc4LR4+kUGtcFbH8E8/6DKedMrIkFTpxl8PeJ2aQDwOrGGqXhSPnoeh
-alDc15pOrwWzpnGUnHGzUGAKxxOdOAeGAqjpqGkmGJCrTLBPI6s6T4TY386f4Wlv
-u9dC12tE5Met7m1BX3JacQg3s3llpFmglDf3AC8NwpJy2tA4ctsUqEXEXSp9t7TW
-xO6szRNEt8kr3UMAJfphuWlqWCMRt6czj1Z1WfXNKddGtworZbbTQm8Vsrh7++/p
-XVPVNFonAgMBAAGjgdgwgdUwHwYDVR0jBBgwFoAUUzLRs89/+uDxoF2FTpLSnkUd
-tE8wHQYDVR0OBBYEFK29mHo0tCb3+sQmVO8DveAky1QaMA4GA1UdDwEB/wQEAwIB
-BjAPBgNVHRMBAf8EBTADAQH/MBEGCWCGSAGG+EIBAQQEAwIBAjAgBgNVHSUEGTAX
-BgorBgEEAYI3CgMDBglghkgBhvhCBAEwPQYDVR0fBDYwNDAyoDCgLoYsaHR0cDov
-L2NybC51c2VydHJ1c3QuY29tL1VUTi1EQVRBQ29ycFNHQy5jcmwwDQYJKoZIhvcN
-AQEFBQADggEBAMbuUxdoFLJRIh6QWA2U/b3xcOWGLcM2MY9USEbnLQg3vGwKYOEO
-rVE04BKT6b64q7gmtOmWPSiPrmQH/uAB7MXjkesYoPF1ftsK5p+R26+udd8jkWjd
-FwBaS/9kbHDrARrQkNnHptZt9hPk/7XJ0h4qy7ElQyZ42TCbTg0evmnv3+r+LbPM
-+bDdtRTKkdSytaX7ARmjR3mfnYyVhzT4HziS2jamEfpr62vp3EV4FTkG101B5CHI
-3C+H0be/SGB1pWLLJN47YaApIKa+xWycxOkKaSLvkTr6Jq/RW0GnOuL4OAdCq8Fb
-+M5tug8EPzI0rNwEKNdwMBQmBsTkm5jVz3g=
------END CERTIFICATE-----
- 3 s:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN - DATACorp SGC
-   i:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN - DATACorp SGC
------BEGIN CERTIFICATE-----
-MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCB
-kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
-Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
-dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw
-IFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBaMIGTMQswCQYDVQQG
-EwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD
-VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cu
-dXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjAN
-BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6
-E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZ
-D0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK
-4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykq
-lXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulW
-bfXv33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB
-o4GrMIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT
-MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3Js
-LnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUEIzAhBggr
-BgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3DQEBBQUAA4IB
-AQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft
-Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyj
-j98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVH
-KWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv
-2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwPDPafepE39peC4N1xaf92P2BNPM/3
-mfnGV/TJVTl4uix5yaaIK/QI
------END CERTIFICATE-----
index be926ea..a5b8c46 100644 (file)
@@ -1898,6 +1898,23 @@ def _run_suite(suite):
         raise TestFailed(err)
 
 
+def _match_test(test):
+    global match_tests
+
+    if match_tests is None:
+        return True
+    test_id = test.id()
+
+    for match_test in match_tests:
+        if fnmatch.fnmatchcase(test_id, match_test):
+            return True
+
+        for name in test_id.split("."):
+            if fnmatch.fnmatchcase(name, match_test):
+                return True
+    return False
+
+
 def run_unittest(*classes):
     """Run tests from unittest.TestCase-derived classes."""
     valid_types = (unittest.TestSuite, unittest.TestCase)
@@ -1912,20 +1929,7 @@ def run_unittest(*classes):
             suite.addTest(cls)
         else:
             suite.addTest(unittest.makeSuite(cls))
-    def case_pred(test):
-        if match_tests is None:
-            return True
-        test_id = test.id()
-
-        for match_test in match_tests:
-            if fnmatch.fnmatchcase(test_id, match_test):
-                return True
-
-            for name in test_id.split("."):
-                if fnmatch.fnmatchcase(name, match_test):
-                    return True
-        return False
-    _filter_suite(suite, case_pred)
+    _filter_suite(suite, _match_test)
     _run_suite(suite)
 
 #=======================================================================
@@ -2049,7 +2053,6 @@ def reap_children():
     stick around to hog resources and create problems when looking
     for refleaks.
     """
-
     # Reap all our dead child processes so we don't leave zombies around.
     # These hog resources and might be causing some of the buildbots to die.
     if hasattr(os, 'waitpid'):
@@ -2060,6 +2063,8 @@ def reap_children():
                 pid, status = os.waitpid(any_process, os.WNOHANG)
                 if pid == 0:
                     break
+                print("Warning -- reap_children() reaped child process %s"
+                      % pid, file=sys.stderr)
             except:
                 break
 
index 492a84a..27781a2 100644 (file)
@@ -258,8 +258,8 @@ class EventLoopTestsMixin:
         if not self.loop.is_closed():
             test_utils.run_briefly(self.loop)
 
-        self.loop.close()
-        gc.collect()
+        self.doCleanups()
+        support.gc_collect()
         super().tearDown()
 
     def test_run_until_complete_nesting(self):
@@ -1980,19 +1980,26 @@ class SubprocessTestsMixin:
 
     @unittest.skipIf(sys.platform == 'win32', "Don't have SIGHUP")
     def test_subprocess_send_signal(self):
-        prog = os.path.join(os.path.dirname(__file__), 'echo.py')
-
-        connect = self.loop.subprocess_exec(
-                        functools.partial(MySubprocessProtocol, self.loop),
-                        sys.executable, prog)
-        transp, proto = self.loop.run_until_complete(connect)
-        self.assertIsInstance(proto, MySubprocessProtocol)
-        self.loop.run_until_complete(proto.connected)
-
-        transp.send_signal(signal.SIGHUP)
-        self.loop.run_until_complete(proto.completed)
-        self.assertEqual(-signal.SIGHUP, proto.returncode)
-        transp.close()
+        # bpo-31034: Make sure that we get the default signal handler (killing
+        # the process). The parent process may have decided to ignore SIGHUP,
+        # and signal handlers are inherited.
+        old_handler = signal.signal(signal.SIGHUP, signal.SIG_DFL)
+        try:
+            prog = os.path.join(os.path.dirname(__file__), 'echo.py')
+
+            connect = self.loop.subprocess_exec(
+                            functools.partial(MySubprocessProtocol, self.loop),
+                            sys.executable, prog)
+            transp, proto = self.loop.run_until_complete(connect)
+            self.assertIsInstance(proto, MySubprocessProtocol)
+            self.loop.run_until_complete(proto.connected)
+
+            transp.send_signal(signal.SIGHUP)
+            self.loop.run_until_complete(proto.completed)
+            self.assertEqual(-signal.SIGHUP, proto.returncode)
+            transp.close()
+        finally:
+            signal.signal(signal.SIGHUP, old_handler)
 
     def test_subprocess_stderr(self):
         prog = os.path.join(os.path.dirname(__file__), 'echo2.py')
index 5d4b2d2..4320a90 100644 (file)
@@ -1,6 +1,7 @@
 """Tests for futures.py."""
 
 import concurrent.futures
+import gc
 import re
 import sys
 import threading
@@ -19,9 +20,11 @@ except ImportError:
 def _fakefunc(f):
     return f
 
+
 def first_cb():
     pass
 
+
 def last_cb():
     pass
 
@@ -97,8 +100,8 @@ class DuckTests(test_utils.TestCase):
 
 class BaseFutureTests:
 
-    def _new_future(self, loop=None):
-        raise NotImplementedError
+    def _new_future(self,  *args, **kwargs):
+        return self.cls(*args, **kwargs)
 
     def setUp(self):
         super().setUp()
@@ -144,6 +147,39 @@ class BaseFutureTests:
         # Make sure Future doesn't accept a positional argument
         self.assertRaises(TypeError, self._new_future, 42)
 
+    def test_uninitialized(self):
+        fut = self.cls.__new__(self.cls, loop=self.loop)
+        self.assertRaises(asyncio.InvalidStateError, fut.result)
+        fut = self.cls.__new__(self.cls, loop=self.loop)
+        self.assertRaises(asyncio.InvalidStateError, fut.exception)
+        fut = self.cls.__new__(self.cls, loop=self.loop)
+        with self.assertRaises((RuntimeError, AttributeError)):
+            fut.set_result(None)
+        fut = self.cls.__new__(self.cls, loop=self.loop)
+        with self.assertRaises((RuntimeError, AttributeError)):
+            fut.set_exception(Exception)
+        fut = self.cls.__new__(self.cls, loop=self.loop)
+        with self.assertRaises((RuntimeError, AttributeError)):
+            fut.cancel()
+        fut = self.cls.__new__(self.cls, loop=self.loop)
+        with self.assertRaises((RuntimeError, AttributeError)):
+            fut.add_done_callback(lambda f: None)
+        fut = self.cls.__new__(self.cls, loop=self.loop)
+        with self.assertRaises((RuntimeError, AttributeError)):
+            fut.remove_done_callback(lambda f: None)
+        fut = self.cls.__new__(self.cls, loop=self.loop)
+        with self.assertRaises((RuntimeError, AttributeError)):
+            fut._schedule_callbacks()
+        fut = self.cls.__new__(self.cls, loop=self.loop)
+        try:
+            repr(fut)
+        except AttributeError:
+            pass
+        fut = self.cls.__new__(self.cls, loop=self.loop)
+        fut.cancelled()
+        fut.done()
+        iter(fut)
+
     def test_cancel(self):
         f = self._new_future(loop=self.loop)
         self.assertTrue(f.cancel())
@@ -377,6 +413,7 @@ class BaseFutureTests:
         self.assertTrue(asyncio.isfuture(f2))
         self.assertEqual(res, 'oi')
         self.assertNotEqual(ident, threading.get_ident())
+        ex.shutdown(wait=True)
 
     def test_wrap_future_future(self):
         f1 = self._new_future(loop=self.loop)
@@ -392,6 +429,7 @@ class BaseFutureTests:
             f1 = ex.submit(run, 'oi')
             f2 = asyncio.wrap_future(f1)
             self.assertIs(self.loop, f2._loop)
+            ex.shutdown(wait=True)
 
     def test_wrap_future_cancel(self):
         f1 = concurrent.futures.Future()
@@ -483,19 +521,24 @@ class BaseFutureTests:
                           Exception("elephant"), Exception("elephant"))
         self.assertRaises(TypeError, fi.throw, list)
 
+    def test_future_del_collect(self):
+        class Evil:
+            def __del__(self):
+                gc.collect()
+
+        for i in range(100):
+            fut = self._new_future(loop=self.loop)
+            fut.set_result(Evil())
+
 
 @unittest.skipUnless(hasattr(futures, '_CFuture'),
                      'requires the C _asyncio module')
 class CFutureTests(BaseFutureTests, test_utils.TestCase):
-
-    def _new_future(self,  *args, **kwargs):
-        return futures._CFuture(*args, **kwargs)
+    cls = getattr(futures, '_CFuture')
 
 
 class PyFutureTests(BaseFutureTests, test_utils.TestCase):
-
-    def _new_future(self, *args, **kwargs):
-        return futures._PyFuture(*args, **kwargs)
+    cls = futures._PyFuture
 
 
 class BaseFutureDoneCallbackTests():
@@ -593,7 +636,7 @@ class BaseFutureDoneCallbackTests():
 
         fut.remove_done_callback(evil())
 
-    def test_schedule_callbacks_list_mutation(self):
+    def test_schedule_callbacks_list_mutation_1(self):
         # see http://bugs.python.org/issue28963 for details
 
         def mut(f):
@@ -606,6 +649,28 @@ class BaseFutureDoneCallbackTests():
         fut.set_result(1)
         test_utils.run_briefly(self.loop)
 
+    def test_schedule_callbacks_list_mutation_2(self):
+        # see http://bugs.python.org/issue30828 for details
+
+        fut = self._new_future()
+        fut.add_done_callback(str)
+
+        for _ in range(63):
+            fut.add_done_callback(id)
+
+        max_extra_cbs = 100
+        extra_cbs = 0
+
+        class evil:
+            def __eq__(self, other):
+                nonlocal extra_cbs
+                extra_cbs += 1
+                if extra_cbs < max_extra_cbs:
+                    fut.add_done_callback(id)
+                return False
+
+        fut.remove_done_callback(evil())
+
 
 @unittest.skipUnless(hasattr(futures, '_CFuture'),
                      'requires the C _asyncio module')
index 4dfc612..d76da66 100644 (file)
@@ -529,7 +529,6 @@ class BaseProactorEventLoopTests(test_utils.TestCase):
             self.loop._loop_self_reading)
 
     def test_loop_self_reading_exception(self):
-        self.loop.close = mock.Mock()
         self.loop.call_exception_handler = mock.Mock()
         self.proactor.recv.side_effect = OSError()
         self.loop._loop_self_reading()
index 6bf7862..c50b3e4 100644 (file)
@@ -24,16 +24,14 @@ MOCK_ANY = mock.ANY
 
 class TestBaseSelectorEventLoop(BaseSelectorEventLoop):
 
-    def close(self):
-        # Don't call the close() method of the parent class, because the
-        # selector is mocked
-        self._closed = True
-
     def _make_self_pipe(self):
         self._ssock = mock.Mock()
         self._csock = mock.Mock()
         self._internal_fds += 1
 
+    def _close_self_pipe(self):
+        pass
+
 
 def list_to_buffer(l=()):
     return bytearray().join(l)
index 2e14a8a..e8822c3 100644 (file)
@@ -166,25 +166,32 @@ class SubprocessMixin:
 
     @unittest.skipIf(sys.platform == 'win32', "Don't have SIGHUP")
     def test_send_signal(self):
-        code = 'import time; print("sleeping", flush=True); time.sleep(3600)'
-        args = [sys.executable, '-c', code]
-        create = asyncio.create_subprocess_exec(*args,
-                                                stdout=subprocess.PIPE,
-                                                loop=self.loop)
-        proc = self.loop.run_until_complete(create)
-
-        @asyncio.coroutine
-        def send_signal(proc):
-            # basic synchronization to wait until the program is sleeping
-            line = yield from proc.stdout.readline()
-            self.assertEqual(line, b'sleeping\n')
+        # bpo-31034: Make sure that we get the default signal handler (killing
+        # the process). The parent process may have decided to ignore SIGHUP,
+        # and signal handlers are inherited.
+        old_handler = signal.signal(signal.SIGHUP, signal.SIG_DFL)
+        try:
+            code = 'import time; print("sleeping", flush=True); time.sleep(3600)'
+            args = [sys.executable, '-c', code]
+            create = asyncio.create_subprocess_exec(*args,
+                                                    stdout=subprocess.PIPE,
+                                                    loop=self.loop)
+            proc = self.loop.run_until_complete(create)
 
-            proc.send_signal(signal.SIGHUP)
-            returncode = (yield from proc.wait())
-            return returncode
-
-        returncode = self.loop.run_until_complete(send_signal(proc))
-        self.assertEqual(-signal.SIGHUP, returncode)
+            @asyncio.coroutine
+            def send_signal(proc):
+                # basic synchronization to wait until the program is sleeping
+                line = yield from proc.stdout.readline()
+                self.assertEqual(line, b'sleeping\n')
+
+                proc.send_signal(signal.SIGHUP)
+                returncode = (yield from proc.wait())
+                return returncode
+
+            returncode = self.loop.run_until_complete(send_signal(proc))
+            self.assertEqual(-signal.SIGHUP, returncode)
+        finally:
+            signal.signal(signal.SIGHUP, old_handler)
 
     def prepare_broken_pipe_test(self):
         # buffer large enough to feed the whole pipe buffer
index 195a1ed..243faf6 100644 (file)
@@ -3,6 +3,7 @@
 import collections
 import contextlib
 import functools
+import gc
 import io
 import os
 import re
@@ -92,6 +93,20 @@ class BaseTaskTests:
         self.loop.set_task_factory(self.new_task)
         self.loop.create_future = lambda: self.new_future(self.loop)
 
+    def test_task_del_collect(self):
+        class Evil:
+            def __del__(self):
+                gc.collect()
+
+        @asyncio.coroutine
+        def run():
+            return Evil()
+
+        self.loop.run_until_complete(
+            asyncio.gather(*[
+                self.new_task(self.loop, run()) for _ in range(100)
+            ], loop=self.loop))
+
     def test_other_loop_future(self):
         other_loop = asyncio.new_event_loop()
         fut = self.new_future(other_loop)
index 1afcae1..c72eef1 100644 (file)
@@ -118,7 +118,9 @@ class ProactorTests(test_utils.TestCase):
 
         self.assertEqual(done, False)
         self.assertFalse(fut.result())
-        self.assertTrue(0.48 < elapsed < 0.9, elapsed)
+        # bpo-31008: Tolerate only 450 ms (at least 500 ms expected),
+        # because of bad clock resolution on Windows
+        self.assertTrue(0.45 <= elapsed <= 0.9, elapsed)
 
         _overlapped.SetEvent(event)
 
index dc2f716..07edf22 100644 (file)
@@ -433,7 +433,10 @@ class FileWrapperTest(unittest.TestCase):
         f = asyncore.file_wrapper(fd)
         os.close(fd)
 
-        f.close()
+        os.close(f.fd)  # file_wrapper dupped fd
+        with self.assertRaises(OSError):
+            f.close()
+
         self.assertEqual(f.fd, -1)
         # calling close twice should not fail
         f.close()
index 8f34d72..9baa62a 100644 (file)
@@ -405,6 +405,10 @@ class TestAudioop(unittest.TestCase):
             self.assertEqual(audioop.ratecv(datas[w], w, 1, 8000, 8000, None, 30, 10)[0],
                              expected[w])
 
+        self.assertRaises(TypeError, audioop.ratecv, b'', 1, 1, 8000, 8000, 42)
+        self.assertRaises(TypeError, audioop.ratecv,
+                          b'', 1, 1, 8000, 8000, (1, (42,)))
+
     def test_reverse(self):
         for w in 1, 2, 3, 4:
             self.assertEqual(audioop.reverse(b'', w), b'')
index 416316c..49c4a53 100644 (file)
@@ -151,6 +151,8 @@ class BuiltinTest(unittest.TestCase):
         self.assertRaises(TypeError, __import__, 1, 2, 3, 4)
         self.assertRaises(ValueError, __import__, '')
         self.assertRaises(TypeError, __import__, 'sys', name='sys')
+        # embedded null character
+        self.assertRaises(ModuleNotFoundError, __import__, 'string\x00')
 
     def test_abs(self):
         # int
@@ -1002,6 +1004,10 @@ class BuiltinTest(unittest.TestCase):
             self.assertEqual(fp.read(300), 'XXX'*100)
             self.assertEqual(fp.read(1000), 'YYY'*100)
 
+        # embedded null bytes and characters
+        self.assertRaises(ValueError, open, 'a\x00b')
+        self.assertRaises(ValueError, open, b'a\x00b')
+
     def test_open_default_encoding(self):
         old_environ = dict(os.environ)
         try:
@@ -1554,6 +1560,10 @@ class PtyTests(unittest.TestCase):
             self.fail("got %d lines in pipe but expected 2, child output was:\n%s"
                       % (len(lines), child_output))
         os.close(fd)
+
+        # Wait until the child process completes
+        os.waitpid(pid, 0)
+
         return lines
 
     def check_input_tty(self, prompt, terminal_input, stdio_encoding=None):
index 9f9df9d..6853748 100644 (file)
@@ -106,6 +106,10 @@ import sys
 import threading
 import unittest
 import weakref
+try:
+    import ctypes
+except ImportError:
+    ctypes = None
 from test.support import (run_doctest, run_unittest, cpython_only,
                           check_impl_detail)
 
@@ -214,8 +218,7 @@ class CodeWeakRefTest(unittest.TestCase):
         self.assertTrue(self.called)
 
 
-if check_impl_detail(cpython=True):
-    import ctypes
+if check_impl_detail(cpython=True) and ctypes is not None:
     py = ctypes.pythonapi
     freefunc = ctypes.CFUNCTYPE(None,ctypes.c_voidp)
 
@@ -311,7 +314,7 @@ def test_main(verbose=None):
     from test import test_code
     run_doctest(test_code, verbose)
     tests = [CodeTest, CodeConstsTest, CodeWeakRefTest]
-    if check_impl_detail(cpython=True):
+    if check_impl_detail(cpython=True) and ctypes is not None:
         tests.append(CoExtra)
     run_unittest(*tests)
 
index 23e95b2..5ddce09 100644 (file)
@@ -11,6 +11,7 @@ test.support.import_module('threading')
 
 from test.support.script_helper import assert_python_ok
 
+import itertools
 import os
 import sys
 import threading
@@ -59,10 +60,25 @@ class MyObject(object):
         pass
 
 
+def make_dummy_object(_):
+    return MyObject()
+
+
+class BaseTestCase(unittest.TestCase):
+    def setUp(self):
+        self._thread_key = test.support.threading_setup()
+
+    def tearDown(self):
+        test.support.reap_children()
+        test.support.threading_cleanup(*self._thread_key)
+
+
 class ExecutorMixin:
     worker_count = 5
 
     def setUp(self):
+        super().setUp()
+
         self.t1 = time.time()
         try:
             self.executor = self.executor_type(max_workers=self.worker_count)
@@ -72,11 +88,15 @@ class ExecutorMixin:
 
     def tearDown(self):
         self.executor.shutdown(wait=True)
+        self.executor = None
+
         dt = time.time() - self.t1
         if test.support.verbose:
             print("%.2fs" % dt, end=' ')
         self.assertLess(dt, 60, "synchronization issue: test lasted too long")
 
+        super().tearDown()
+
     def _prime_executor(self):
         # Make sure that the executor is ready to do work before running the
         # tests. This should reduce the probability of timeouts in the tests.
@@ -123,7 +143,7 @@ class ExecutorShutdownTest:
             f.result()
 
 
-class ThreadPoolShutdownTest(ThreadPoolMixin, ExecutorShutdownTest, unittest.TestCase):
+class ThreadPoolShutdownTest(ThreadPoolMixin, ExecutorShutdownTest, BaseTestCase):
     def _prime_executor(self):
         pass
 
@@ -172,14 +192,13 @@ class ThreadPoolShutdownTest(ThreadPoolMixin, ExecutorShutdownTest, unittest.Tes
         del executor
 
         for t in threads:
-            # We don't particularly care what the default name is, just that
-            # it has a default name implying that it is a ThreadPoolExecutor
-            # followed by what looks like a thread number.
-            self.assertRegex(t.name, r'^.*ThreadPoolExecutor.*_[0-4]$')
+            # Ensure that our default name is reasonably sane and unique when
+            # no thread_name_prefix was supplied.
+            self.assertRegex(t.name, r'ThreadPoolExecutor-\d+_[0-4]$')
             t.join()
 
 
-class ProcessPoolShutdownTest(ProcessPoolMixin, ExecutorShutdownTest, unittest.TestCase):
+class ProcessPoolShutdownTest(ProcessPoolMixin, ExecutorShutdownTest, BaseTestCase):
     def _prime_executor(self):
         pass
 
@@ -208,11 +227,14 @@ class ProcessPoolShutdownTest(ProcessPoolMixin, ExecutorShutdownTest, unittest.T
         list(executor.map(abs, range(-5, 5)))
         queue_management_thread = executor._queue_management_thread
         processes = executor._processes
+        call_queue = executor._call_queue
         del executor
 
         queue_management_thread.join()
         for p in processes.values():
             p.join()
+        call_queue.close()
+        call_queue.join_thread()
 
 
 class WaitTests:
@@ -316,7 +338,7 @@ class WaitTests:
         self.assertEqual(set([future2]), pending)
 
 
-class ThreadPoolWaitTests(ThreadPoolMixin, WaitTests, unittest.TestCase):
+class ThreadPoolWaitTests(ThreadPoolMixin, WaitTests, BaseTestCase):
 
     def test_pending_calls_race(self):
         # Issue #14406: multi-threaded race condition when waiting on all
@@ -334,7 +356,7 @@ class ThreadPoolWaitTests(ThreadPoolMixin, WaitTests, unittest.TestCase):
             sys.setswitchinterval(oldswitchinterval)
 
 
-class ProcessPoolWaitTests(ProcessPoolMixin, WaitTests, unittest.TestCase):
+class ProcessPoolWaitTests(ProcessPoolMixin, WaitTests, BaseTestCase):
     pass
 
 
@@ -378,16 +400,51 @@ class AsCompletedTests:
     def test_duplicate_futures(self):
         # Issue 20367. Duplicate futures should not raise exceptions or give
         # duplicate responses.
+        # Issue #31641: accept arbitrary iterables.
         future1 = self.executor.submit(time.sleep, 2)
-        completed = [f for f in futures.as_completed([future1,future1])]
+        completed = [
+            f for f in futures.as_completed(itertools.repeat(future1, 3))
+        ]
         self.assertEqual(len(completed), 1)
 
+    def test_free_reference_yielded_future(self):
+        # Issue #14406: Generator should not keep references
+        # to finished futures.
+        futures_list = [Future() for _ in range(8)]
+        futures_list.append(create_future(state=CANCELLED_AND_NOTIFIED))
+        futures_list.append(create_future(state=FINISHED, result=42))
 
-class ThreadPoolAsCompletedTests(ThreadPoolMixin, AsCompletedTests, unittest.TestCase):
+        with self.assertRaises(futures.TimeoutError):
+            for future in futures.as_completed(futures_list, timeout=0):
+                futures_list.remove(future)
+                wr = weakref.ref(future)
+                del future
+                self.assertIsNone(wr())
+
+        futures_list[0].set_result("test")
+        for future in futures.as_completed(futures_list):
+            futures_list.remove(future)
+            wr = weakref.ref(future)
+            del future
+            self.assertIsNone(wr())
+            if futures_list:
+                futures_list[0].set_result("test")
+
+    def test_correct_timeout_exception_msg(self):
+        futures_list = [CANCELLED_AND_NOTIFIED_FUTURE, PENDING_FUTURE,
+                        RUNNING_FUTURE, SUCCESSFUL_FUTURE]
+
+        with self.assertRaises(futures.TimeoutError) as cm:
+            list(futures.as_completed(futures_list, timeout=0))
+
+        self.assertEqual(str(cm.exception), '2 (of 4) futures unfinished')
+
+
+class ThreadPoolAsCompletedTests(ThreadPoolMixin, AsCompletedTests, BaseTestCase):
     pass
 
 
-class ProcessPoolAsCompletedTests(ProcessPoolMixin, AsCompletedTests, unittest.TestCase):
+class ProcessPoolAsCompletedTests(ProcessPoolMixin, AsCompletedTests, BaseTestCase):
     pass
 
 
@@ -407,6 +464,10 @@ class ExecutorTest:
                 list(self.executor.map(pow, range(10), range(10))),
                 list(map(pow, range(10), range(10))))
 
+        self.assertEqual(
+                list(self.executor.map(pow, range(10), range(10), chunksize=3)),
+                list(map(pow, range(10), range(10))))
+
     def test_map_exception(self):
         i = self.executor.map(divmod, [1, 1, 1, 1], [2, 3, 0, 5])
         self.assertEqual(i.__next__(), (0, 1))
@@ -457,8 +518,16 @@ class ExecutorTest:
                                         "than 0"):
                 self.executor_type(max_workers=number)
 
+    def test_free_reference(self):
+        # Issue #14406: Result iterator should not keep an internal
+        # reference to result objects.
+        for obj in self.executor.map(make_dummy_object, range(10)):
+            wr = weakref.ref(obj)
+            del obj
+            self.assertIsNone(wr())
+
 
-class ThreadPoolExecutorTest(ThreadPoolMixin, ExecutorTest, unittest.TestCase):
+class ThreadPoolExecutorTest(ThreadPoolMixin, ExecutorTest, BaseTestCase):
     def test_map_submits_without_iteration(self):
         """Tests verifying issue 11777."""
         finished = []
@@ -475,7 +544,7 @@ class ThreadPoolExecutorTest(ThreadPoolMixin, ExecutorTest, unittest.TestCase):
                          (os.cpu_count() or 1) * 5)
 
 
-class ProcessPoolExecutorTest(ProcessPoolMixin, ExecutorTest, unittest.TestCase):
+class ProcessPoolExecutorTest(ProcessPoolMixin, ExecutorTest, BaseTestCase):
     def test_killed_child(self):
         # When a child process is abruptly terminated, the whole pool gets
         # "broken".
@@ -531,7 +600,7 @@ class ProcessPoolExecutorTest(ProcessPoolMixin, ExecutorTest, unittest.TestCase)
                       f1.getvalue())
 
 
-class FutureTests(unittest.TestCase):
+class FutureTests(BaseTestCase):
     def test_done_callback_with_result(self):
         callback_result = None
         def fn(callback_future):
@@ -711,6 +780,7 @@ class FutureTests(unittest.TestCase):
         t.start()
 
         self.assertEqual(f1.result(timeout=5), 42)
+        t.join()
 
     def test_result_with_cancel(self):
         # TODO(brian@sweetapp.com): This test is timing dependent.
@@ -724,6 +794,7 @@ class FutureTests(unittest.TestCase):
         t.start()
 
         self.assertRaises(futures.CancelledError, f1.result, timeout=5)
+        t.join()
 
     def test_exception_with_timeout(self):
         self.assertRaises(futures.TimeoutError,
@@ -752,6 +823,7 @@ class FutureTests(unittest.TestCase):
         t.start()
 
         self.assertTrue(isinstance(f1.exception(timeout=5), OSError))
+        t.join()
 
 @test.support.reap_threads
 def test_main():
index 52e887c..e3f1cd8 100644 (file)
@@ -16,6 +16,12 @@ class WithWeakref(object):
 class WithPrivate(object):
     __slots__ = ('__spam',)
 
+class _WithLeadingUnderscoreAndPrivate(object):
+    __slots__ = ('__spam',)
+
+class ___(object):
+    __slots__ = ('__spam',)
+
 class WithSingleString(object):
     __slots__ = 'spam'
 
@@ -104,6 +110,10 @@ class CopyRegTestCase(unittest.TestCase):
         self.assertEqual(copyreg._slotnames(WithWeakref), [])
         expected = ['_WithPrivate__spam']
         self.assertEqual(copyreg._slotnames(WithPrivate), expected)
+        expected = ['_WithLeadingUnderscoreAndPrivate__spam']
+        self.assertEqual(copyreg._slotnames(_WithLeadingUnderscoreAndPrivate),
+                         expected)
+        self.assertEqual(copyreg._slotnames(___), ['__spam'])
         self.assertEqual(copyreg._slotnames(WithSingleString), ['spam'])
         expected = ['eggs', 'spam']
         expected.sort()
index 3d8c50b..0d0b160 100644 (file)
@@ -81,7 +81,7 @@ class TestCurses(unittest.TestCase):
         win2 = curses.newwin(15,15, 5,5)
 
         for meth in [stdscr.addch, stdscr.addstr]:
-            for args in [('a'), ('a', curses.A_BOLD),
+            for args in [('a',), ('a', curses.A_BOLD),
                          (4,4, 'a'), (5,5, 'a', curses.A_BOLD)]:
                 with self.subTest(meth=meth.__qualname__, args=args):
                     meth(*args)
@@ -194,6 +194,15 @@ class TestCurses(unittest.TestCase):
         self.assertRaises(ValueError, stdscr.instr, -2)
         self.assertRaises(ValueError, stdscr.instr, 2, 3, -2)
 
+    def test_embedded_null_chars(self):
+        # reject embedded null bytes and characters
+        stdscr = self.stdscr
+        for arg in ['a', b'a']:
+            with self.subTest(arg=arg):
+                self.assertRaises(ValueError, stdscr.addstr, 'a\0')
+                self.assertRaises(ValueError, stdscr.addnstr, 'a\0', 1)
+                self.assertRaises(ValueError, stdscr.insstr, 'a\0')
+                self.assertRaises(ValueError, stdscr.insnstr, 'a\0', 1)
 
     def test_module_funcs(self):
         "Test module-level functions"
index 242e1bb..d659f36 100644 (file)
@@ -20,7 +20,7 @@ test_suffixes = ["_Pure", "_Fast"]
 # XXX(gb) First run all the _Pure tests, then all the _Fast tests.  You might
 # not believe this, but in spite of all the sys.modules trickery running a _Pure
 # test last will leave a mix of pure and native datetime stuff lying around.
-test_classes = []
+all_test_classes = []
 
 for module, suffix in zip(test_modules, test_suffixes):
     test_classes = []
@@ -32,8 +32,10 @@ for module, suffix in zip(test_modules, test_suffixes):
         elif issubclass(cls, unittest.TestSuite):
             suit = cls()
             test_classes.extend(type(test) for test in suit)
+    test_classes = sorted(set(test_classes), key=lambda cls: cls.__qualname__)
     for cls in test_classes:
-        cls.__name__ = name + suffix
+        cls.__name__ += suffix
+        cls.__qualname__ += suffix
         @classmethod
         def setUpClass(cls_, module=module):
             cls_._save_sys_modules = sys.modules.copy()
@@ -46,9 +48,10 @@ for module, suffix in zip(test_modules, test_suffixes):
             sys.modules.update(cls_._save_sys_modules)
         cls.setUpClass = setUpClass
         cls.tearDownClass = tearDownClass
+    all_test_classes.extend(test_classes)
 
 def test_main():
-    run_unittest(*test_classes)
+    run_unittest(*all_test_classes)
 
 if __name__ == "__main__":
     test_main()
index 617a37e..57094ad 100644 (file)
@@ -1614,6 +1614,9 @@ class ThreadingTest(unittest.TestCase):
         for sig in Signals[self.decimal]:
             self.assertFalse(DefaultContext.flags[sig])
 
+        th1.join()
+        th2.join()
+
         DefaultContext.prec = save_prec
         DefaultContext.Emax = save_emax
         DefaultContext.Emin = save_emin
index 26ece69..e0ec87d 100644 (file)
@@ -2711,5 +2711,17 @@ class TestFolding(TestEmailBase):
         self._test(parser.get_unstructured('xxx   ' + 'y'*77),
                    'xxx  \n ' + 'y'*77 + '\n')
 
+    def test_long_filename_attachment(self):
+        folded = self.policy.fold('Content-Disposition', 'attachment; filename="TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TES.txt"')
+        self.assertEqual(
+            'Content-Disposition: attachment;\n filename="TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TES.txt"\n',
+            folded
+        )
+        folded = self.policy.fold('Content-Disposition', 'attachment; filename="TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_T.txt"')
+        self.assertEqual(
+            'Content-Disposition: attachment;\n filename="TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_TEST_T.txt"\n',
+            folded
+        )
+
 if __name__ == '__main__':
     unittest.main()
index 13a89fc..eaea8ee 100644 (file)
@@ -2291,6 +2291,26 @@ class TestIntFlag(unittest.TestCase):
             self.assertIs(type(e), Perm)
 
 
+    def test_programatic_function_from_empty_list(self):
+        Perm = enum.IntFlag('Perm', [])
+        lst = list(Perm)
+        self.assertEqual(len(lst), len(Perm))
+        self.assertEqual(len(Perm), 0, Perm)
+        Thing = enum.Enum('Thing', [])
+        lst = list(Thing)
+        self.assertEqual(len(lst), len(Thing))
+        self.assertEqual(len(Thing), 0, Thing)
+
+
+    def test_programatic_function_from_empty_tuple(self):
+        Perm = enum.IntFlag('Perm', ())
+        lst = list(Perm)
+        self.assertEqual(len(lst), len(Perm))
+        self.assertEqual(len(Perm), 0, Perm)
+        Thing = enum.Enum('Thing', ())
+        self.assertEqual(len(lst), len(Thing))
+        self.assertEqual(len(Thing), 0, Thing)
+
     def test_containment(self):
         Perm = self.Perm
         R, W, X = Perm
index 960fc0f..53851cb 100644 (file)
@@ -156,6 +156,34 @@ class ExceptionTests(unittest.TestCase):
         ckmsg(s, "'continue' not properly in loop")
         ckmsg("continue\n", "'continue' not properly in loop")
 
+    def testSyntaxErrorMissingParens(self):
+        def ckmsg(src, msg, exception=SyntaxError):
+            try:
+                compile(src, '<fragment>', 'exec')
+            except exception as e:
+                if e.msg != msg:
+                    self.fail("expected %s, got %s" % (msg, e.msg))
+            else:
+                self.fail("failed to get expected SyntaxError")
+
+        s = '''print "old style"'''
+        ckmsg(s, "Missing parentheses in call to 'print'. "
+                 "Did you mean print(\"old style\")?")
+
+        s = '''print "old style",'''
+        ckmsg(s, "Missing parentheses in call to 'print'. "
+                 "Did you mean print(\"old style\", end=\" \")?")
+
+        s = '''exec "old style"'''
+        ckmsg(s, "Missing parentheses in call to 'exec'")
+
+        # should not apply to subclasses, see issue #31161
+        s = '''if True:\nprint "No indent"'''
+        ckmsg(s, "expected an indented block", IndentationError)
+
+        s = '''if True:\n        print()\n\texec "mixed tabs and spaces"'''
+        ckmsg(s, "inconsistent use of tabs and spaces in indentation", TabError)
+
     def testSyntaxErrorOffset(self):
         def check(src, lineno, offset):
             with self.assertRaises(SyntaxError) as cm:
index 043df01..2c18483 100644 (file)
@@ -163,6 +163,10 @@ right error message? (Also check with other iterables.)
     Traceback (most recent call last):
       ...
     TypeError: myerror
+    >>> g(*range(1), *(broken() for i in range(1)))
+    Traceback (most recent call last):
+      ...
+    TypeError: myerror
 
     >>> class BrokenIterable1:
     ...     def __iter__(self):
@@ -172,6 +176,10 @@ right error message? (Also check with other iterables.)
     Traceback (most recent call last):
       ...
     TypeError: myerror
+    >>> g(*range(1), *BrokenIterable1())
+    Traceback (most recent call last):
+      ...
+    TypeError: myerror
 
     >>> class BrokenIterable2:
     ...     def __iter__(self):
@@ -182,6 +190,10 @@ right error message? (Also check with other iterables.)
     Traceback (most recent call last):
       ...
     TypeError: myerror
+    >>> g(*range(1), *BrokenIterable2())
+    Traceback (most recent call last):
+      ...
+    TypeError: myerror
 
     >>> class BrokenSequence:
     ...     def __getitem__(self, idx):
@@ -191,6 +203,10 @@ right error message? (Also check with other iterables.)
     Traceback (most recent call last):
       ...
     TypeError: myerror
+    >>> g(*range(1), *BrokenSequence())
+    Traceback (most recent call last):
+      ...
+    TypeError: myerror
 
 Make sure that the function doesn't stomp the dictionary
 
index 6491f45..c5ca50c 100644 (file)
@@ -613,6 +613,12 @@ class IEEEFormatTestCase(unittest.TestCase):
                           ('<f', LE_FLOAT_NAN)]:
             struct.unpack(fmt, data)
 
+    @support.requires_IEEE_754
+    def test_serialized_float_rounding(self):
+        from _testcapi import FLT_MAX
+        self.assertEqual(struct.pack("<f", 3.40282356e38), struct.pack("<f", FLT_MAX))
+        self.assertEqual(struct.pack("<f", -3.40282356e38), struct.pack("<f", -FLT_MAX))
+
 class FormatTestCase(unittest.TestCase):
 
     def test_format(self):
index 3d762b5..5e7efe2 100644 (file)
@@ -70,6 +70,253 @@ f'{a * x()}'"""
         # Make sure x was called.
         self.assertTrue(x.called)
 
+    def test_ast_line_numbers(self):
+        expr = """
+a = 10
+f'{a * x()}'"""
+        t = ast.parse(expr)
+        self.assertEqual(type(t), ast.Module)
+        self.assertEqual(len(t.body), 2)
+        # check `a = 10`
+        self.assertEqual(type(t.body[0]), ast.Assign)
+        self.assertEqual(t.body[0].lineno, 2)
+        # check `f'...'`
+        self.assertEqual(type(t.body[1]), ast.Expr)
+        self.assertEqual(type(t.body[1].value), ast.JoinedStr)
+        self.assertEqual(len(t.body[1].value.values), 1)
+        self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
+        self.assertEqual(t.body[1].lineno, 3)
+        self.assertEqual(t.body[1].value.lineno, 3)
+        self.assertEqual(t.body[1].value.values[0].lineno, 3)
+        # check the binop location
+        binop = t.body[1].value.values[0].value
+        self.assertEqual(type(binop), ast.BinOp)
+        self.assertEqual(type(binop.left), ast.Name)
+        self.assertEqual(type(binop.op), ast.Mult)
+        self.assertEqual(type(binop.right), ast.Call)
+        self.assertEqual(binop.lineno, 3)
+        self.assertEqual(binop.left.lineno, 3)
+        self.assertEqual(binop.right.lineno, 3)
+        self.assertEqual(binop.col_offset, 3)
+        self.assertEqual(binop.left.col_offset, 3)
+        self.assertEqual(binop.right.col_offset, 7)
+
+    def test_ast_line_numbers_multiple_formattedvalues(self):
+        expr = """
+f'no formatted values'
+f'eggs {a * x()} spam {b + y()}'"""
+        t = ast.parse(expr)
+        self.assertEqual(type(t), ast.Module)
+        self.assertEqual(len(t.body), 2)
+        # check `f'no formatted value'`
+        self.assertEqual(type(t.body[0]), ast.Expr)
+        self.assertEqual(type(t.body[0].value), ast.JoinedStr)
+        self.assertEqual(t.body[0].lineno, 2)
+        # check `f'...'`
+        self.assertEqual(type(t.body[1]), ast.Expr)
+        self.assertEqual(type(t.body[1].value), ast.JoinedStr)
+        self.assertEqual(len(t.body[1].value.values), 4)
+        self.assertEqual(type(t.body[1].value.values[0]), ast.Str)
+        self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
+        self.assertEqual(type(t.body[1].value.values[2]), ast.Str)
+        self.assertEqual(type(t.body[1].value.values[3]), ast.FormattedValue)
+        self.assertEqual(t.body[1].lineno, 3)
+        self.assertEqual(t.body[1].value.lineno, 3)
+        self.assertEqual(t.body[1].value.values[0].lineno, 3)
+        self.assertEqual(t.body[1].value.values[1].lineno, 3)
+        self.assertEqual(t.body[1].value.values[2].lineno, 3)
+        self.assertEqual(t.body[1].value.values[3].lineno, 3)
+        # check the first binop location
+        binop1 = t.body[1].value.values[1].value
+        self.assertEqual(type(binop1), ast.BinOp)
+        self.assertEqual(type(binop1.left), ast.Name)
+        self.assertEqual(type(binop1.op), ast.Mult)
+        self.assertEqual(type(binop1.right), ast.Call)
+        self.assertEqual(binop1.lineno, 3)
+        self.assertEqual(binop1.left.lineno, 3)
+        self.assertEqual(binop1.right.lineno, 3)
+        self.assertEqual(binop1.col_offset, 8)
+        self.assertEqual(binop1.left.col_offset, 8)
+        self.assertEqual(binop1.right.col_offset, 12)
+        # check the second binop location
+        binop2 = t.body[1].value.values[3].value
+        self.assertEqual(type(binop2), ast.BinOp)
+        self.assertEqual(type(binop2.left), ast.Name)
+        self.assertEqual(type(binop2.op), ast.Add)
+        self.assertEqual(type(binop2.right), ast.Call)
+        self.assertEqual(binop2.lineno, 3)
+        self.assertEqual(binop2.left.lineno, 3)
+        self.assertEqual(binop2.right.lineno, 3)
+        self.assertEqual(binop2.col_offset, 23)
+        self.assertEqual(binop2.left.col_offset, 23)
+        self.assertEqual(binop2.right.col_offset, 27)
+
+    def test_ast_line_numbers_nested(self):
+        expr = """
+a = 10
+f'{a * f"-{x()}-"}'"""
+        t = ast.parse(expr)
+        self.assertEqual(type(t), ast.Module)
+        self.assertEqual(len(t.body), 2)
+        # check `a = 10`
+        self.assertEqual(type(t.body[0]), ast.Assign)
+        self.assertEqual(t.body[0].lineno, 2)
+        # check `f'...'`
+        self.assertEqual(type(t.body[1]), ast.Expr)
+        self.assertEqual(type(t.body[1].value), ast.JoinedStr)
+        self.assertEqual(len(t.body[1].value.values), 1)
+        self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
+        self.assertEqual(t.body[1].lineno, 3)
+        self.assertEqual(t.body[1].value.lineno, 3)
+        self.assertEqual(t.body[1].value.values[0].lineno, 3)
+        # check the binop location
+        binop = t.body[1].value.values[0].value
+        self.assertEqual(type(binop), ast.BinOp)
+        self.assertEqual(type(binop.left), ast.Name)
+        self.assertEqual(type(binop.op), ast.Mult)
+        self.assertEqual(type(binop.right), ast.JoinedStr)
+        self.assertEqual(binop.lineno, 3)
+        self.assertEqual(binop.left.lineno, 3)
+        self.assertEqual(binop.right.lineno, 3)
+        self.assertEqual(binop.col_offset, 3)
+        self.assertEqual(binop.left.col_offset, 3)
+        self.assertEqual(binop.right.col_offset, 7)
+        # check the nested call location
+        self.assertEqual(len(binop.right.values), 3)
+        self.assertEqual(type(binop.right.values[0]), ast.Str)
+        self.assertEqual(type(binop.right.values[1]), ast.FormattedValue)
+        self.assertEqual(type(binop.right.values[2]), ast.Str)
+        self.assertEqual(binop.right.values[0].lineno, 3)
+        self.assertEqual(binop.right.values[1].lineno, 3)
+        self.assertEqual(binop.right.values[2].lineno, 3)
+        call = binop.right.values[1].value
+        self.assertEqual(type(call), ast.Call)
+        self.assertEqual(call.lineno, 3)
+        self.assertEqual(call.col_offset, 11)
+
+    def test_ast_line_numbers_duplicate_expression(self):
+        """Duplicate expression
+
+        NOTE: this is currently broken, always sets location of the first
+        expression.
+        """
+        expr = """
+a = 10
+f'{a * x()} {a * x()} {a * x()}'
+"""
+        t = ast.parse(expr)
+        self.assertEqual(type(t), ast.Module)
+        self.assertEqual(len(t.body), 2)
+        # check `a = 10`
+        self.assertEqual(type(t.body[0]), ast.Assign)
+        self.assertEqual(t.body[0].lineno, 2)
+        # check `f'...'`
+        self.assertEqual(type(t.body[1]), ast.Expr)
+        self.assertEqual(type(t.body[1].value), ast.JoinedStr)
+        self.assertEqual(len(t.body[1].value.values), 5)
+        self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)
+        self.assertEqual(type(t.body[1].value.values[1]), ast.Str)
+        self.assertEqual(type(t.body[1].value.values[2]), ast.FormattedValue)
+        self.assertEqual(type(t.body[1].value.values[3]), ast.Str)
+        self.assertEqual(type(t.body[1].value.values[4]), ast.FormattedValue)
+        self.assertEqual(t.body[1].lineno, 3)
+        self.assertEqual(t.body[1].value.lineno, 3)
+        self.assertEqual(t.body[1].value.values[0].lineno, 3)
+        self.assertEqual(t.body[1].value.values[1].lineno, 3)
+        self.assertEqual(t.body[1].value.values[2].lineno, 3)
+        self.assertEqual(t.body[1].value.values[3].lineno, 3)
+        self.assertEqual(t.body[1].value.values[4].lineno, 3)
+        # check the first binop location
+        binop = t.body[1].value.values[0].value
+        self.assertEqual(type(binop), ast.BinOp)
+        self.assertEqual(type(binop.left), ast.Name)
+        self.assertEqual(type(binop.op), ast.Mult)
+        self.assertEqual(type(binop.right), ast.Call)
+        self.assertEqual(binop.lineno, 3)
+        self.assertEqual(binop.left.lineno, 3)
+        self.assertEqual(binop.right.lineno, 3)
+        self.assertEqual(binop.col_offset, 3)
+        self.assertEqual(binop.left.col_offset, 3)
+        self.assertEqual(binop.right.col_offset, 7)
+        # check the second binop location
+        binop = t.body[1].value.values[2].value
+        self.assertEqual(type(binop), ast.BinOp)
+        self.assertEqual(type(binop.left), ast.Name)
+        self.assertEqual(type(binop.op), ast.Mult)
+        self.assertEqual(type(binop.right), ast.Call)
+        self.assertEqual(binop.lineno, 3)
+        self.assertEqual(binop.left.lineno, 3)
+        self.assertEqual(binop.right.lineno, 3)
+        self.assertEqual(binop.col_offset, 3)  # FIXME: this is wrong
+        self.assertEqual(binop.left.col_offset, 3)  # FIXME: this is wrong
+        self.assertEqual(binop.right.col_offset, 7)  # FIXME: this is wrong
+        # check the third binop location
+        binop = t.body[1].value.values[4].value
+        self.assertEqual(type(binop), ast.BinOp)
+        self.assertEqual(type(binop.left), ast.Name)
+        self.assertEqual(type(binop.op), ast.Mult)
+        self.assertEqual(type(binop.right), ast.Call)
+        self.assertEqual(binop.lineno, 3)
+        self.assertEqual(binop.left.lineno, 3)
+        self.assertEqual(binop.right.lineno, 3)
+        self.assertEqual(binop.col_offset, 3)  # FIXME: this is wrong
+        self.assertEqual(binop.left.col_offset, 3)  # FIXME: this is wrong
+        self.assertEqual(binop.right.col_offset, 7)  # FIXME: this is wrong
+
+    def test_ast_line_numbers_multiline_fstring(self):
+        # FIXME: This test demonstrates invalid behavior due to JoinedStr's
+        # immediate child nodes containing the wrong lineno.  The enclosed
+        # expressions have valid line information and column offsets.
+        # See bpo-16806 and bpo-30465 for details.
+        expr = """
+a = 10
+f'''
+  {a
+     *
+       x()}
+non-important content
+'''
+"""
+        t = ast.parse(expr)
+        self.assertEqual(type(t), ast.Module)
+        self.assertEqual(len(t.body), 2)
+        # check `a = 10`
+        self.assertEqual(type(t.body[0]), ast.Assign)
+        self.assertEqual(t.body[0].lineno, 2)
+        # check `f'...'`
+        self.assertEqual(type(t.body[1]), ast.Expr)
+        self.assertEqual(type(t.body[1].value), ast.JoinedStr)
+        self.assertEqual(len(t.body[1].value.values), 3)
+        self.assertEqual(type(t.body[1].value.values[0]), ast.Str)
+        self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)
+        self.assertEqual(type(t.body[1].value.values[2]), ast.Str)
+        # NOTE: the following invalid behavior is described in bpo-16806.
+        # - line number should be the *first* line (3), not the *last* (8)
+        # - column offset should not be -1
+        self.assertEqual(t.body[1].lineno, 8)
+        self.assertEqual(t.body[1].value.lineno, 8)
+        self.assertEqual(t.body[1].value.values[0].lineno, 8)
+        self.assertEqual(t.body[1].value.values[1].lineno, 8)
+        self.assertEqual(t.body[1].value.values[2].lineno, 8)
+        self.assertEqual(t.body[1].col_offset, -1)
+        self.assertEqual(t.body[1].value.col_offset, -1)
+        self.assertEqual(t.body[1].value.values[0].col_offset, -1)
+        self.assertEqual(t.body[1].value.values[1].col_offset, -1)
+        self.assertEqual(t.body[1].value.values[2].col_offset, -1)
+        # NOTE: the following lineno information and col_offset is correct for
+        # expressions within FormattedValues.
+        binop = t.body[1].value.values[1].value
+        self.assertEqual(type(binop), ast.BinOp)
+        self.assertEqual(type(binop.left), ast.Name)
+        self.assertEqual(type(binop.op), ast.Mult)
+        self.assertEqual(type(binop.right), ast.Call)
+        self.assertEqual(binop.lineno, 4)
+        self.assertEqual(binop.left.lineno, 4)
+        self.assertEqual(binop.right.lineno, 6)
+        self.assertEqual(binop.col_offset, 3)
+        self.assertEqual(binop.left.col_offset, 3)
+        self.assertEqual(binop.right.col_offset, 7)
+
     def test_docstring(self):
         def f():
             f'''Not a docstring'''
@@ -786,5 +1033,6 @@ f'{a * x()}'"""
         self.assertEqual(eval('f"\\\n"'), '')
         self.assertEqual(eval('f"\\\r"'), '')
 
+
 if __name__ == '__main__':
     unittest.main()
index 12fabc5..b593313 100644 (file)
@@ -470,6 +470,9 @@ class TestFTPClass(TestCase):
     def tearDown(self):
         self.client.close()
         self.server.stop()
+        # Explicitly clear the attribute to prevent dangling thread
+        self.server = None
+        asyncore.close_all(ignore_all=True)
 
     def check_data(self, received, expected):
         self.assertEqual(len(received), len(expected))
@@ -484,6 +487,9 @@ class TestFTPClass(TestCase):
         self.assertEqual(self.client.sanitize('PASS 12345'), repr('PASS *****'))
 
     def test_exceptions(self):
+        self.assertRaises(ValueError, self.client.sendcmd, 'echo 40\r\n0')
+        self.assertRaises(ValueError, self.client.sendcmd, 'echo 40\n0')
+        self.assertRaises(ValueError, self.client.sendcmd, 'echo 40\r0')
         self.assertRaises(ftplib.error_temp, self.client.sendcmd, 'echo 400')
         self.assertRaises(ftplib.error_temp, self.client.sendcmd, 'echo 499')
         self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'echo 500')
@@ -492,7 +498,8 @@ class TestFTPClass(TestCase):
 
     def test_all_errors(self):
         exceptions = (ftplib.error_reply, ftplib.error_temp, ftplib.error_perm,
-                      ftplib.error_proto, ftplib.Error, OSError, EOFError)
+                      ftplib.error_proto, ftplib.Error, OSError,
+                      EOFError)
         for x in exceptions:
             try:
                 raise x('exception not included in all_errors set')
@@ -795,6 +802,9 @@ class TestIPv6Environment(TestCase):
     def tearDown(self):
         self.client.close()
         self.server.stop()
+        # Explicitly clear the attribute to prevent dangling thread
+        self.server = None
+        asyncore.close_all(ignore_all=True)
 
     def test_af(self):
         self.assertEqual(self.client.af, socket.AF_INET6)
@@ -853,6 +863,9 @@ class TestTLS_FTPClass(TestCase):
     def tearDown(self):
         self.client.close()
         self.server.stop()
+        # Explicitly clear the attribute to prevent dangling thread
+        self.server = None
+        asyncore.close_all(ignore_all=True)
 
     def test_control_connection(self):
         self.assertNotIsInstance(self.client.sock, ssl.SSLSocket)
@@ -975,6 +988,8 @@ class TestTimeouts(TestCase):
     def tearDown(self):
         ftplib.FTP.port = self.old_port
         self.server_thread.join()
+        # Explicitly clear the attribute to prevent dangling thread
+        self.server_thread = None
 
     def server(self):
         # This method sets the evt 3 times:
index 8a194aa..d14a6ee 100644 (file)
@@ -377,6 +377,12 @@ class Float_TestCase(unittest.TestCase):
         r = getargs_f(NAN)
         self.assertNotEqual(r, r)
 
+    @support.requires_IEEE_754
+    def test_f_rounding(self):
+        from _testcapi import getargs_f
+        self.assertEqual(getargs_f(3.40282356e38), FLT_MAX)
+        self.assertEqual(getargs_f(-3.40282356e38), -FLT_MAX)
+
     def test_d(self):
         from _testcapi import getargs_d
         self.assertEqual(getargs_d(4.25), 4.25)
index a852443..1873845 100644 (file)
@@ -1,6 +1,7 @@
 import os
 import base64
 import gettext
+import locale
 import unittest
 
 from test import support
@@ -452,6 +453,122 @@ class PluralFormsTestCase(GettextBaseTest):
         self.assertRaises(TypeError, f, object())
 
 
+class LGettextTestCase(GettextBaseTest):
+    def setUp(self):
+        GettextBaseTest.setUp(self)
+        self.mofile = MOFILE
+
+    def test_lgettext(self):
+        lgettext = gettext.lgettext
+        ldgettext = gettext.ldgettext
+        self.assertEqual(lgettext('mullusk'), b'bacon')
+        self.assertEqual(lgettext('spam'), b'spam')
+        self.assertEqual(ldgettext('gettext', 'mullusk'), b'bacon')
+        self.assertEqual(ldgettext('gettext', 'spam'), b'spam')
+
+    def test_lgettext_2(self):
+        with open(self.mofile, 'rb') as fp:
+            t = gettext.GNUTranslations(fp)
+        lgettext = t.lgettext
+        self.assertEqual(lgettext('mullusk'), b'bacon')
+        self.assertEqual(lgettext('spam'), b'spam')
+
+    def test_lgettext_bind_textdomain_codeset(self):
+        lgettext = gettext.lgettext
+        ldgettext = gettext.ldgettext
+        saved_codeset = gettext.bind_textdomain_codeset('gettext')
+        try:
+            gettext.bind_textdomain_codeset('gettext', 'utf-16')
+            self.assertEqual(lgettext('mullusk'), 'bacon'.encode('utf-16'))
+            self.assertEqual(lgettext('spam'), 'spam'.encode('utf-16'))
+            self.assertEqual(ldgettext('gettext', 'mullusk'), 'bacon'.encode('utf-16'))
+            self.assertEqual(ldgettext('gettext', 'spam'), 'spam'.encode('utf-16'))
+        finally:
+            del gettext._localecodesets['gettext']
+            gettext.bind_textdomain_codeset('gettext', saved_codeset)
+
+    def test_lgettext_output_encoding(self):
+        with open(self.mofile, 'rb') as fp:
+            t = gettext.GNUTranslations(fp)
+        lgettext = t.lgettext
+        t.set_output_charset('utf-16')
+        self.assertEqual(lgettext('mullusk'), 'bacon'.encode('utf-16'))
+        self.assertEqual(lgettext('spam'), 'spam'.encode('utf-16'))
+
+    def test_lngettext(self):
+        lngettext = gettext.lngettext
+        ldngettext = gettext.ldngettext
+        x = lngettext('There is %s file', 'There are %s files', 1)
+        self.assertEqual(x, b'Hay %s fichero')
+        x = lngettext('There is %s file', 'There are %s files', 2)
+        self.assertEqual(x, b'Hay %s ficheros')
+        x = lngettext('There is %s directory', 'There are %s directories', 1)
+        self.assertEqual(x, b'There is %s directory')
+        x = lngettext('There is %s directory', 'There are %s directories', 2)
+        self.assertEqual(x, b'There are %s directories')
+        x = ldngettext('gettext', 'There is %s file', 'There are %s files', 1)
+        self.assertEqual(x, b'Hay %s fichero')
+        x = ldngettext('gettext', 'There is %s file', 'There are %s files', 2)
+        self.assertEqual(x, b'Hay %s ficheros')
+        x = ldngettext('gettext', 'There is %s directory', 'There are %s directories', 1)
+        self.assertEqual(x, b'There is %s directory')
+        x = ldngettext('gettext', 'There is %s directory', 'There are %s directories', 2)
+        self.assertEqual(x, b'There are %s directories')
+
+    def test_lngettext_2(self):
+        with open(self.mofile, 'rb') as fp:
+            t = gettext.GNUTranslations(fp)
+        lngettext = t.lngettext
+        x = lngettext('There is %s file', 'There are %s files', 1)
+        self.assertEqual(x, b'Hay %s fichero')
+        x = lngettext('There is %s file', 'There are %s files', 2)
+        self.assertEqual(x, b'Hay %s ficheros')
+        x = lngettext('There is %s directory', 'There are %s directories', 1)
+        self.assertEqual(x, b'There is %s directory')
+        x = lngettext('There is %s directory', 'There are %s directories', 2)
+        self.assertEqual(x, b'There are %s directories')
+
+    def test_lngettext_bind_textdomain_codeset(self):
+        lngettext = gettext.lngettext
+        ldngettext = gettext.ldngettext
+        saved_codeset = gettext.bind_textdomain_codeset('gettext')
+        try:
+            gettext.bind_textdomain_codeset('gettext', 'utf-16')
+            x = lngettext('There is %s file', 'There are %s files', 1)
+            self.assertEqual(x, 'Hay %s fichero'.encode('utf-16'))
+            x = lngettext('There is %s file', 'There are %s files', 2)
+            self.assertEqual(x, 'Hay %s ficheros'.encode('utf-16'))
+            x = lngettext('There is %s directory', 'There are %s directories', 1)
+            self.assertEqual(x, 'There is %s directory'.encode('utf-16'))
+            x = lngettext('There is %s directory', 'There are %s directories', 2)
+            self.assertEqual(x, 'There are %s directories'.encode('utf-16'))
+            x = ldngettext('gettext', 'There is %s file', 'There are %s files', 1)
+            self.assertEqual(x, 'Hay %s fichero'.encode('utf-16'))
+            x = ldngettext('gettext', 'There is %s file', 'There are %s files', 2)
+            self.assertEqual(x, 'Hay %s ficheros'.encode('utf-16'))
+            x = ldngettext('gettext', 'There is %s directory', 'There are %s directories', 1)
+            self.assertEqual(x, 'There is %s directory'.encode('utf-16'))
+            x = ldngettext('gettext', 'There is %s directory', 'There are %s directories', 2)
+            self.assertEqual(x, 'There are %s directories'.encode('utf-16'))
+        finally:
+            del gettext._localecodesets['gettext']
+            gettext.bind_textdomain_codeset('gettext', saved_codeset)
+
+    def test_lngettext_output_encoding(self):
+        with open(self.mofile, 'rb') as fp:
+            t = gettext.GNUTranslations(fp)
+        lngettext = t.lngettext
+        t.set_output_charset('utf-16')
+        x = lngettext('There is %s file', 'There are %s files', 1)
+        self.assertEqual(x, 'Hay %s fichero'.encode('utf-16'))
+        x = lngettext('There is %s file', 'There are %s files', 2)
+        self.assertEqual(x, 'Hay %s ficheros'.encode('utf-16'))
+        x = lngettext('There is %s directory', 'There are %s directories', 1)
+        self.assertEqual(x, 'There is %s directory'.encode('utf-16'))
+        x = lngettext('There is %s directory', 'There are %s directories', 2)
+        self.assertEqual(x, 'There are %s directories'.encode('utf-16'))
+
+
 class GNUTranslationParsingTest(GettextBaseTest):
     def test_plural_form_error_issue17898(self):
         with open(MOFILE, 'wb') as fp:
@@ -469,13 +586,10 @@ class UnicodeTranslationsTest(GettextBaseTest):
         self._ = self.t.gettext
 
     def test_unicode_msgid(self):
-        unless = self.assertTrue
-        unless(isinstance(self._(''), str))
-        unless(isinstance(self._(''), str))
+        self.assertIsInstance(self._(''), str)
 
     def test_unicode_msgstr(self):
-        eq = self.assertEqual
-        eq(self._('ab\xde'), '\xa4yz')
+        self.assertEqual(self._('ab\xde'), '\xa4yz')
 
 
 class WeirdMetadataTest(GettextBaseTest):
@@ -544,7 +658,7 @@ if __name__ == '__main__':
 # The original version was automatically generated from the sources with
 # pygettext. Later it was manually modified to add plural forms support.
 
-'''
+b'''
 # Dummy translation for the Python test_gettext.py module.
 # Copyright (C) 2001 Python Software Foundation
 # Barry Warsaw <barry@python.org>, 2000.
@@ -604,7 +718,7 @@ msgstr[1] "Hay %s ficheros"
 # Here's the second example po file example, used to generate the UMO_DATA
 # containing utf-8 encoded Unicode strings
 
-'''
+b'''
 # Dummy translation for the Python test_gettext.py module.
 # Copyright (C) 2001 Python Software Foundation
 # Barry Warsaw <barry@python.org>, 2000.
@@ -627,7 +741,7 @@ msgstr "\xc2\xa4yz"
 
 # Here's the third example po file, used to generate MMO_DATA
 
-'''
+b'''
 msgid ""
 msgstr ""
 "Project-Id-Version: No Project 0.0\n"
@@ -646,7 +760,7 @@ msgstr ""
 # messages.po, used for bug 17898
 #
 
-'''
+b'''
 # test file for http://bugs.python.org/issue17898
 msgid ""
 msgstr ""
index 69095a3..e511947 100644 (file)
@@ -50,6 +50,8 @@ class GroupDatabaseTestCase(unittest.TestCase):
         self.assertRaises(TypeError, grp.getgrgid)
         self.assertRaises(TypeError, grp.getgrnam)
         self.assertRaises(TypeError, grp.getgrall, 42)
+        # embedded null character
+        self.assertRaises(ValueError, grp.getgrnam, 'a\x00b')
 
         # try to get some errors
         bynames = {}
index f748b46..647719e 100644 (file)
@@ -750,28 +750,28 @@ class HashLibTestCase(unittest.TestCase):
         hasher = hashlib.sha1()
         num_threads = 5
         smallest_data = b'swineflu'
-        data = smallest_data*200000
+        data = smallest_data * 200000
         expected_hash = hashlib.sha1(data*num_threads).hexdigest()
 
-        def hash_in_chunks(chunk_size, event):
+        def hash_in_chunks(chunk_size):
             index = 0
             while index < len(data):
-                hasher.update(data[index:index+chunk_size])
+                hasher.update(data[index:index + chunk_size])
                 index += chunk_size
-            event.set()
 
-        events = []
+        threads = []
         for threadnum in range(num_threads):
-            chunk_size = len(data) // (10**threadnum)
+            chunk_size = len(data) // (10 ** threadnum)
             self.assertGreater(chunk_size, 0)
             self.assertEqual(chunk_size % len(smallest_data), 0)
-            event = threading.Event()
-            events.append(event)
-            threading.Thread(target=hash_in_chunks,
-                             args=(chunk_size, event)).start()
-
-        for event in events:
-            event.wait()
+            thread = threading.Thread(target=hash_in_chunks,
+                                      args=(chunk_size,))
+            threads.append(thread)
+
+        for thread in threads:
+            thread.start()
+        for thread in threads:
+            thread.join()
 
         self.assertEqual(expected_hash, hasher.hexdigest())
 
index 4e93144..22b3bf5 100644 (file)
@@ -52,6 +52,7 @@ class TestServerThread(threading.Thread):
 
     def stop(self):
         self.server.shutdown()
+        self.join()
 
 
 class BaseTestCase(unittest.TestCase):
index 4ece365..6f35f49 100644 (file)
@@ -314,6 +314,10 @@ class ImportTests(unittest.TestCase):
         loader.get_data(imp.__file__)  # File should be closed
         loader.get_data(imp.__file__)  # Will need to create a newly opened file
 
+    def test_load_source(self):
+        with self.assertRaisesRegex(ValueError, 'embedded null'):
+            imp.load_source(__name__, __file__ + "\0")
+
 
 class ReloadTests(unittest.TestCase):
 
index 4aca9e5..e811121 100644 (file)
@@ -10,6 +10,8 @@ import py_compile
 import random
 import stat
 import sys
+import threading
+import time
 import unittest
 import unittest.mock as mock
 import textwrap
@@ -22,8 +24,9 @@ from test.support import (
     EnvironmentVarGuard, TESTFN, check_warnings, forget, is_jython,
     make_legacy_pyc, rmtree, run_unittest, swap_attr, swap_item, temp_umask,
     unlink, unload, create_empty_file, cpython_only, TESTFN_UNENCODABLE,
-    temp_dir)
+    temp_dir, DirsOnSysPath)
 from test.support import script_helper
+from test.test_importlib.util import uncache
 
 
 skip_if_dont_write_bytecode = unittest.skipIf(
@@ -350,6 +353,32 @@ class ImportTests(unittest.TestCase):
         with self.assertRaises(ImportError):
             from test_from_import_AttributeError import does_not_exist
 
+    def test_concurrency(self):
+        sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'data'))
+        try:
+            exc = None
+            def run():
+                event.wait()
+                try:
+                    import package
+                except BaseException as e:
+                    nonlocal exc
+                    exc = e
+
+            for i in range(10):
+                event = threading.Event()
+                threads = [threading.Thread(target=run) for x in range(2)]
+                try:
+                    with test.support.start_threads(threads, event.set):
+                        time.sleep(0)
+                finally:
+                    sys.modules.pop('package', None)
+                    sys.modules.pop('package.submodule', None)
+                if exc is not None:
+                    raise exc
+        finally:
+            del sys.path[0]
+
 
 @skip_if_dont_write_bytecode
 class FilePermissionTests(unittest.TestCase):
@@ -612,11 +641,11 @@ class RelativeImportTests(unittest.TestCase):
 
         # Check relative import fails with only __package__ wrong
         ns = dict(__package__='foo', __name__='test.notarealmodule')
-        self.assertRaises(SystemError, check_relative)
+        self.assertRaises(ModuleNotFoundError, check_relative)
 
         # Check relative import fails with __package__ and __name__ wrong
         ns = dict(__package__='foo', __name__='notarealpkg.notarealmodule')
-        self.assertRaises(SystemError, check_relative)
+        self.assertRaises(ModuleNotFoundError, check_relative)
 
         # Check relative import fails with package set to a non-string
         ns = dict(__package__=object())
@@ -631,6 +660,20 @@ class RelativeImportTests(unittest.TestCase):
             self.fail("explicit relative import triggered an "
                       "implicit absolute import")
 
+    def test_import_from_non_package(self):
+        path = os.path.join(os.path.dirname(__file__), 'data', 'package2')
+        with uncache('submodule1', 'submodule2'), DirsOnSysPath(path):
+            with self.assertRaises(ImportError):
+                import submodule1
+            self.assertNotIn('submodule1', sys.modules)
+            self.assertNotIn('submodule2', sys.modules)
+
+    def test_import_from_unloaded_package(self):
+        with uncache('package2', 'package2.submodule1', 'package2.submodule2'), \
+             DirsOnSysPath(os.path.join(os.path.dirname(__file__), 'data')):
+            import package2.submodule1
+            package2.submodule1.submodule2
+
 
 class OverridingImportBuiltinTests(unittest.TestCase):
     def test_override_builtin(self):
diff --git a/Lib/test/test_import/data/package/__init__.py b/Lib/test/test_import/data/package/__init__.py
new file mode 100644 (file)
index 0000000..a4f2bc3
--- /dev/null
@@ -0,0 +1,2 @@
+import package.submodule
+package.submodule
diff --git a/Lib/test/test_import/data/package/submodule.py b/Lib/test/test_import/data/package/submodule.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Lib/test/test_import/data/package2/submodule1.py b/Lib/test/test_import/data/package2/submodule1.py
new file mode 100644 (file)
index 0000000..0698ed6
--- /dev/null
@@ -0,0 +1,3 @@
+import sys
+sys.modules.pop(__package__, None)
+from . import submodule2
diff --git a/Lib/test/test_import/data/package2/submodule2.py b/Lib/test/test_import/data/package2/submodule2.py
new file mode 100644 (file)
index 0000000..e69de29
index 7f64548..b26634e 100644 (file)
@@ -81,7 +81,7 @@ class Using__package__:
 
     def test_bad__package__(self):
         globals = {'__package__': '<not real>'}
-        with self.assertRaises(SystemError):
+        with self.assertRaises(ModuleNotFoundError):
             self.__import__('', globals, {}, ['relimport'], 1)
 
     def test_bunk__package__(self):
index 8c91ad2..08c041e 100644 (file)
@@ -3163,6 +3163,14 @@ class TextIOWrapperTest(unittest.TestCase):
         t = self.TextIOWrapper(self.StringIO('a'))
         self.assertRaises(TypeError, t.read)
 
+    def test_illegal_encoder(self):
+        # Issue 31271: Calling write() while the return value of encoder's
+        # encode() is invalid shouldn't cause an assertion failure.
+        rot13 = codecs.lookup("rot13")
+        with support.swap_attr(rot13, '_is_text_encoding', True):
+            t = io.TextIOWrapper(io.BytesIO(b'foo'), encoding="rot13")
+        self.assertRaises(TypeError, t.write, 'bar')
+
     def test_illegal_decoder(self):
         # Issue #17106
         # Bypass the early encoding check added in issue 20404
@@ -3183,6 +3191,26 @@ class TextIOWrapperTest(unittest.TestCase):
         t = _make_illegal_wrapper()
         self.assertRaises(TypeError, t.read)
 
+        # Issue 31243: calling read() while the return value of decoder's
+        # getstate() is invalid should neither crash the interpreter nor
+        # raise a SystemError.
+        def _make_very_illegal_wrapper(getstate_ret_val):
+            class BadDecoder:
+                def getstate(self):
+                    return getstate_ret_val
+            def _get_bad_decoder(dummy):
+                return BadDecoder()
+            quopri = codecs.lookup("quopri")
+            with support.swap_attr(quopri, 'incrementaldecoder',
+                                   _get_bad_decoder):
+                return _make_illegal_wrapper()
+        t = _make_very_illegal_wrapper(42)
+        self.assertRaises(TypeError, t.read, 42)
+        t = _make_very_illegal_wrapper(())
+        self.assertRaises(TypeError, t.read, 42)
+        t = _make_very_illegal_wrapper((1, 2))
+        self.assertRaises(TypeError, t.read, 42)
+
     def _check_create_at_shutdown(self, **kwargs):
         # Issue #20037: creating a TextIOWrapper at shutdown
         # shouldn't crash the interpreter.
@@ -3410,6 +3438,7 @@ class IncrementalNewlineDecoderTest(unittest.TestCase):
         decoder = codecs.getincrementaldecoder("utf-8")()
         decoder = self.IncrementalNewlineDecoder(decoder, translate=True)
         self.check_newline_decoding_utf8(decoder)
+        self.assertRaises(TypeError, decoder.setstate, 42)
 
     def test_newline_bytes(self):
         # Issue 5433: Excessive optimization in IncrementalNewlineDecoder
index 109a246..56f1882 100644 (file)
@@ -1,6 +1,11 @@
 from test.test_json import CTest
 
 
+class BadBool:
+    def __bool__(self):
+        1/0
+
+
 class TestSpeedups(CTest):
     def test_scanstring(self):
         self.assertEqual(self.json.decoder.scanstring.__module__, "_json")
@@ -17,8 +22,29 @@ class TestDecode(CTest):
     def test_make_scanner(self):
         self.assertRaises(AttributeError, self.json.scanner.c_make_scanner, 1)
 
+    def test_bad_bool_args(self):
+        def test(value):
+            self.json.decoder.JSONDecoder(strict=BadBool()).decode(value)
+        self.assertRaises(ZeroDivisionError, test, '""')
+        self.assertRaises(ZeroDivisionError, test, '{}')
+
+
+class TestEncode(CTest):
     def test_make_encoder(self):
         self.assertRaises(TypeError, self.json.encoder.c_make_encoder,
             (True, False),
             b"\xCD\x7D\x3D\x4E\x12\x4C\xF9\x79\xD7\x52\xBA\x82\xF2\x27\x4A\x7D\xA0\xCA\x75",
             None)
+
+    def test_bad_bool_args(self):
+        def test(name):
+            self.json.encoder.JSONEncoder(**{name: BadBool()}).encode({'a': 1})
+        self.assertRaises(ZeroDivisionError, test, 'skipkeys')
+        self.assertRaises(ZeroDivisionError, test, 'ensure_ascii')
+        self.assertRaises(ZeroDivisionError, test, 'check_circular')
+        self.assertRaises(ZeroDivisionError, test, 'allow_nan')
+        self.assertRaises(ZeroDivisionError, test, 'sort_keys')
+
+    def test_unsortable_keys(self):
+        with self.assertRaises(TypeError):
+            self.json.encoder.JSONEncoder(sort_keys=True).encode({'a': 1, 1: 'a'})
index 1f8265f..ddb169f 100644 (file)
@@ -139,7 +139,7 @@ def test_main(verbose=None):
         import gc
         counts = [None] * 5
         for i in range(len(counts)):
-            support.run_doctest(test_genexps, verbose)
+            support.run_doctest(test_listcomps, verbose)
             gc.collect()
             counts[i] = sys.gettotalrefcount()
         print(counts)
index 99fab58..650d737 100644 (file)
@@ -339,9 +339,14 @@ class TestCollation(unittest.TestCase):
         self.assertLess(locale.strcoll('a', 'b'), 0)
         self.assertEqual(locale.strcoll('a', 'a'), 0)
         self.assertGreater(locale.strcoll('b', 'a'), 0)
+        # embedded null character
+        self.assertRaises(ValueError, locale.strcoll, 'a\0', 'a')
+        self.assertRaises(ValueError, locale.strcoll, 'a', 'a\0')
 
     def test_strxfrm(self):
         self.assertLess(locale.strxfrm('a'), locale.strxfrm('b'))
+        # embedded null character
+        self.assertRaises(ValueError, locale.strxfrm, 'a\0')
 
 
 class TestEnUSCollation(BaseLocalizedTest, TestCollation):
index 474affd..25046c3 100644 (file)
@@ -770,6 +770,7 @@ if threading:
             """
             self.close()
             self._thread.join(timeout)
+            asyncore.close_all(map=self._map, ignore_all=True)
             self._thread = None
 
     class ControlMixin(object):
@@ -3954,6 +3955,17 @@ class LoggerAdapterTest(unittest.TestCase):
         self.assertFalse(self.logger.hasHandlers())
         self.assertFalse(self.adapter.hasHandlers())
 
+    def test_nested(self):
+        msg = 'Adapters can be nested, yo.'
+        adapter_adapter = logging.LoggerAdapter(logger=self.adapter, extra=None)
+        adapter_adapter.log(logging.CRITICAL, msg, self.recording)
+
+        self.assertEqual(len(self.recording.records), 1)
+        record = self.recording.records[0]
+        self.assertEqual(record.levelno, logging.CRITICAL)
+        self.assertEqual(record.msg, msg)
+        self.assertEqual(record.args, (self.recording,))
+
 
 class LoggerTest(BaseTest):
 
index 5b590e1..3042456 100644 (file)
@@ -36,15 +36,23 @@ def unistr(data):
 
 class NormalizationTest(unittest.TestCase):
     def test_main(self):
-        part = None
-        part1_data = {}
         # Hit the exception early
         try:
             testdata = open_urlresource(TESTDATAURL, encoding="utf-8",
                                         check=check_version)
+        except PermissionError:
+            self.skipTest(f"Permission error when downloading {TESTDATAURL} "
+                          f"into the test data directory")
         except (OSError, HTTPException):
-            self.skipTest("Could not retrieve " + TESTDATAURL)
-        self.addCleanup(testdata.close)
+            self.fail(f"Could not retrieve {TESTDATAURL}")
+
+        with testdata:
+            self.run_normalization_tests(testdata)
+
+    def run_normalization_tests(self, testdata):
+        part = None
+        part1_data = {}
+
         for line in testdata:
             if '#' in line:
                 line = line.split('#')[0]
index 8612ec9..b65ccb7 100644 (file)
@@ -836,6 +836,30 @@ class EnvironTests(mapping_tests.BasicTestMappingProtocol):
         self.assertIs(cm.exception.args[0], missing)
         self.assertTrue(cm.exception.__suppress_context__)
 
+    def _test_environ_iteration(self, collection):
+        iterator = iter(collection)
+        new_key = "__new_key__"
+
+        next(iterator)  # start iteration over os.environ.items
+
+        # add a new key in os.environ mapping
+        os.environ[new_key] = "test_environ_iteration"
+
+        try:
+            next(iterator)  # force iteration over modified mapping
+            self.assertEqual(os.environ[new_key], "test_environ_iteration")
+        finally:
+            del os.environ[new_key]
+
+    def test_iter_error_when_changing_os_environ(self):
+        self._test_environ_iteration(os.environ)
+
+    def test_iter_error_when_changing_os_environ_items(self):
+        self._test_environ_iteration(os.environ.items())
+
+    def test_iter_error_when_changing_os_environ_values(self):
+        self._test_environ_iteration(os.environ.values())
+
 
 class WalkTests(unittest.TestCase):
     """Tests for os.walk()."""
@@ -1553,6 +1577,27 @@ class ExecTests(unittest.TestCase):
         if os.name != "nt":
             self._test_internal_execvpe(bytes)
 
+    def test_execve_invalid_env(self):
+        args = [sys.executable, '-c', 'pass']
+
+        # null character in the enviroment variable name
+        newenv = os.environ.copy()
+        newenv["FRUIT\0VEGETABLE"] = "cabbage"
+        with self.assertRaises(ValueError):
+            os.execve(args[0], args, newenv)
+
+        # null character in the enviroment variable value
+        newenv = os.environ.copy()
+        newenv["FRUIT"] = "orange\0VEGETABLE=cabbage"
+        with self.assertRaises(ValueError):
+            os.execve(args[0], args, newenv)
+
+        # equal character in the enviroment variable name
+        newenv = os.environ.copy()
+        newenv["FRUIT=ORANGE"] = "lemon"
+        with self.assertRaises(ValueError):
+            os.execve(args[0], args, newenv)
+
 
 @unittest.skipUnless(sys.platform == "win32", "Win32 specific tests")
 class Win32ErrorTests(unittest.TestCase):
@@ -2364,6 +2409,61 @@ class SpawnTests(unittest.TestCase):
         self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, args[0], ('',), {})
         self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, args[0], [''], {})
 
+    def _test_invalid_env(self, spawn):
+        args = [sys.executable, '-c', 'pass']
+
+        # null character in the enviroment variable name
+        newenv = os.environ.copy()
+        newenv["FRUIT\0VEGETABLE"] = "cabbage"
+        try:
+            exitcode = spawn(os.P_WAIT, args[0], args, newenv)
+        except ValueError:
+            pass
+        else:
+            self.assertEqual(exitcode, 127)
+
+        # null character in the enviroment variable value
+        newenv = os.environ.copy()
+        newenv["FRUIT"] = "orange\0VEGETABLE=cabbage"
+        try:
+            exitcode = spawn(os.P_WAIT, args[0], args, newenv)
+        except ValueError:
+            pass
+        else:
+            self.assertEqual(exitcode, 127)
+
+        # equal character in the enviroment variable name
+        newenv = os.environ.copy()
+        newenv["FRUIT=ORANGE"] = "lemon"
+        try:
+            exitcode = spawn(os.P_WAIT, args[0], args, newenv)
+        except ValueError:
+            pass
+        else:
+            self.assertEqual(exitcode, 127)
+
+        # equal character in the enviroment variable value
+        filename = support.TESTFN
+        self.addCleanup(support.unlink, filename)
+        with open(filename, "w") as fp:
+            fp.write('import sys, os\n'
+                     'if os.getenv("FRUIT") != "orange=lemon":\n'
+                     '    raise AssertionError')
+        args = [sys.executable, filename]
+        newenv = os.environ.copy()
+        newenv["FRUIT"] = "orange=lemon"
+        exitcode = spawn(os.P_WAIT, args[0], args, newenv)
+        self.assertEqual(exitcode, 0)
+
+    @requires_os_func('spawnve')
+    def test_spawnve_invalid_env(self):
+        self._test_invalid_env(os.spawnve)
+
+    @requires_os_func('spawnvpe')
+    def test_spawnvpe_invalid_env(self):
+        self._test_invalid_env(os.spawnvpe)
+
+
 # The introduction of this TestCase caused at least two different errors on
 # *nix buildbots. Temporarily skip this to let the buildbots move along.
 @unittest.skip("Skip due to platform/environment differences on *NIX buildbots")
@@ -2521,6 +2621,7 @@ class TestSendfile(unittest.TestCase):
         self.client.close()
         if self.server.running:
             self.server.stop()
+        self.server = None
 
     def sendfile_wrapper(self, sock, file, offset, nbytes, headers=[], trailers=[]):
         """A higher level wrapper representing how an application is
@@ -3302,6 +3403,22 @@ class TestScandir(unittest.TestCase):
         self.assertEqual(entry.path,
                          os.fsencode(os.path.join(self.path, 'file.txt')))
 
+    def test_bytes_like(self):
+        self.create_file("file.txt")
+
+        for cls in bytearray, memoryview:
+            path_bytes = cls(os.fsencode(self.path))
+            with self.assertWarns(DeprecationWarning):
+                entries = list(os.scandir(path_bytes))
+            self.assertEqual(len(entries), 1, entries)
+            entry = entries[0]
+
+            self.assertEqual(entry.name, b'file.txt')
+            self.assertEqual(entry.path,
+                             os.fsencode(os.path.join(self.path, 'file.txt')))
+            self.assertIs(type(entry.name), bytes)
+            self.assertIs(type(entry.path), bytes)
+
     def test_empty_path(self):
         self.assertRaises(FileNotFoundError, os.scandir, '')
 
index e5b16dc..1269199 100644 (file)
@@ -254,6 +254,8 @@ class TestPOP3Class(TestCase):
     def tearDown(self):
         self.client.close()
         self.server.stop()
+        # Explicitly clear the attribute to prevent dangling thread
+        self.server = None
 
     def test_getwelcome(self):
         self.assertEqual(self.client.getwelcome(),
@@ -436,6 +438,8 @@ class TestPOP3_TLSClass(TestPOP3Class):
                 # this exception
                 self.client.close()
         self.server.stop()
+        # Explicitly clear the attribute to prevent dangling thread
+        self.server = None
 
     def test_stls(self):
         self.assertRaises(poplib.error_proto, self.client.stls)
@@ -461,7 +465,8 @@ class TestTimeouts(TestCase):
 
     def tearDown(self):
         self.thread.join()
-        del self.thread  # Clear out any dangling Thread objects.
+        # Explicitly clear the attribute to prevent dangling thread
+        self.thread = None
 
     def server(self, evt, serv):
         serv.listen()
@@ -483,7 +488,7 @@ class TestTimeouts(TestCase):
         finally:
             socket.setdefaulttimeout(None)
         self.assertEqual(pop.sock.gettimeout(), 30)
-        pop.sock.close()
+        pop.close()
 
     def testTimeoutNone(self):
         self.assertIsNone(socket.getdefaulttimeout())
@@ -493,12 +498,12 @@ class TestTimeouts(TestCase):
         finally:
             socket.setdefaulttimeout(None)
         self.assertIsNone(pop.sock.gettimeout())
-        pop.sock.close()
+        pop.close()
 
     def testTimeoutValue(self):
         pop = poplib.POP3(HOST, self.port, timeout=30)
         self.assertEqual(pop.sock.gettimeout(), 30)
-        pop.sock.close()
+        pop.close()
 
 
 def test_main():
index 029d081..9df3206 100644 (file)
@@ -478,6 +478,10 @@ class PosixTester(unittest.TestCase):
         self.assertRaises(TypeError, posix.minor)
         self.assertRaises((ValueError, OverflowError), posix.minor, -1)
 
+        if sys.platform.startswith('freebsd') and dev >= 0x1_0000_0000:
+            self.skipTest("bpo-31044: on FreeBSD CURRENT, minor() truncates "
+                          "64-bit dev to 32-bit")
+
         self.assertEqual(posix.makedev(major, minor), dev)
         self.assertRaises(TypeError, posix.makedev, float(major), minor)
         self.assertRaises(TypeError, posix.makedev, major, float(minor))
@@ -581,17 +585,25 @@ class PosixTester(unittest.TestCase):
         self.assertRaises(OSError, posix.chdir, support.TESTFN)
 
     def test_listdir(self):
-        self.assertTrue(support.TESTFN in posix.listdir(os.curdir))
+        self.assertIn(support.TESTFN, posix.listdir(os.curdir))
 
     def test_listdir_default(self):
         # When listdir is called without argument,
         # it's the same as listdir(os.curdir).
-        self.assertTrue(support.TESTFN in posix.listdir())
+        self.assertIn(support.TESTFN, posix.listdir())
 
     def test_listdir_bytes(self):
         # When listdir is called with a bytes object,
         # the returned strings are of type bytes.
-        self.assertTrue(os.fsencode(support.TESTFN) in posix.listdir(b'.'))
+        self.assertIn(os.fsencode(support.TESTFN), posix.listdir(b'.'))
+
+    def test_listdir_bytes_like(self):
+        for cls in bytearray, memoryview:
+            with self.assertWarns(DeprecationWarning):
+                names = posix.listdir(cls(b'.'))
+            self.assertIn(os.fsencode(support.TESTFN), names)
+            for name in names:
+                self.assertIs(type(name), bytes)
 
     @unittest.skipUnless(posix.listdir in os.supports_fd,
                          "test needs fd support for posix.listdir()")
@@ -751,6 +763,21 @@ class PosixTester(unittest.TestCase):
             self.assertEqual(type(k), item_type)
             self.assertEqual(type(v), item_type)
 
+    @unittest.skipUnless(hasattr(os, "putenv"), "requires os.putenv()")
+    def test_putenv(self):
+        with self.assertRaises(ValueError):
+            os.putenv('FRUIT\0VEGETABLE', 'cabbage')
+        with self.assertRaises(ValueError):
+            os.putenv(b'FRUIT\0VEGETABLE', b'cabbage')
+        with self.assertRaises(ValueError):
+            os.putenv('FRUIT', 'orange\0VEGETABLE=cabbage')
+        with self.assertRaises(ValueError):
+            os.putenv(b'FRUIT', b'orange\0VEGETABLE=cabbage')
+        with self.assertRaises(ValueError):
+            os.putenv('FRUIT=ORANGE', 'lemon')
+        with self.assertRaises(ValueError):
+            os.putenv(b'FRUIT=ORANGE', b'lemon')
+
     @unittest.skipUnless(hasattr(posix, 'getcwd'), 'test needs posix.getcwd()')
     def test_getcwd_long_pathnames(self):
         dirname = 'getcwd-test-directory-0123456789abcdef-01234567890abcdef'
index 7eea349..e6434fe 100644 (file)
@@ -1,4 +1,5 @@
 import unittest
+import sys
 from io import StringIO
 
 from test import support
@@ -128,5 +129,65 @@ class TestPrint(unittest.TestCase):
                 raise RuntimeError
         self.assertRaises(RuntimeError, print, 1, file=noflush(), flush=True)
 
+
+class TestPy2MigrationHint(unittest.TestCase):
+    """Test that correct hint is produced analogous to Python3 syntax,
+    if print statement is executed as in Python 2.
+    """
+
+    def test_normal_string(self):
+        python2_print_str = 'print "Hello World"'
+        with self.assertRaises(SyntaxError) as context:
+            exec(python2_print_str)
+
+        self.assertIn('print("Hello World")', str(context.exception))
+
+    def test_string_with_soft_space(self):
+        python2_print_str = 'print "Hello World",'
+        with self.assertRaises(SyntaxError) as context:
+            exec(python2_print_str)
+
+        self.assertIn('print("Hello World", end=" ")', str(context.exception))
+
+    def test_string_with_excessive_whitespace(self):
+        python2_print_str = 'print  "Hello World", '
+        with self.assertRaises(SyntaxError) as context:
+            exec(python2_print_str)
+
+        self.assertIn('print("Hello World", end=" ")', str(context.exception))
+
+    def test_stream_redirection_hint_for_py2_migration(self):
+        # Test correct hint produced for Py2 redirection syntax
+        with self.assertRaises(TypeError) as context:
+            print >> sys.stderr, "message"
+        self.assertIn('Did you mean "print(<message>, '
+                'file=<output_stream>)"?', str(context.exception))
+
+        # Test correct hint is produced in the case where RHS implements
+        # __rrshift__ but returns NotImplemented
+        with self.assertRaises(TypeError) as context:
+            print >> 42
+        self.assertIn('Did you mean "print(<message>, '
+                'file=<output_stream>)"?', str(context.exception))
+
+        # Test stream redirection hint is specific to print
+        with self.assertRaises(TypeError) as context:
+            max >> sys.stderr
+        self.assertNotIn('Did you mean ', str(context.exception))
+
+        # Test stream redirection hint is specific to rshift
+        with self.assertRaises(TypeError) as context:
+            print << sys.stderr
+        self.assertNotIn('Did you mean', str(context.exception))
+
+        # Ensure right operand implementing rrshift still works
+        class OverrideRRShift:
+            def __rrshift__(self, lhs):
+                return 42 # Force result independent of LHS
+
+        self.assertEqual(print >> OverrideRRShift(), 42)
+
+
+
 if __name__ == "__main__":
     unittest.main()
index ed438d5..0383a67 100644 (file)
@@ -361,7 +361,7 @@ def get_pydoc_html(module):
 def get_pydoc_link(module):
     "Returns a documentation web link of a module"
     dirname = os.path.dirname
-    basedir = dirname(dirname(__file__))
+    basedir = dirname(dirname(os.path.realpath(__file__)))
     doc = pydoc.TextDoc()
     loc = doc.getdocloc(module, basedir=basedir)
     return loc
index 4ccaa39..c686095 100644 (file)
@@ -46,28 +46,27 @@ class _TriggerThread(threading.Thread):
 
 class BlockingTestMixin:
 
-    def tearDown(self):
-        self.t = None
-
     def do_blocking_test(self, block_func, block_args, trigger_func, trigger_args):
-        self.t = _TriggerThread(trigger_func, trigger_args)
-        self.t.start()
-        self.result = block_func(*block_args)
-        # If block_func returned before our thread made the call, we failed!
-        if not self.t.startedEvent.is_set():
-            self.fail("blocking function '%r' appeared not to block" %
-                      block_func)
-        self.t.join(10) # make sure the thread terminates
-        if self.t.is_alive():
-            self.fail("trigger function '%r' appeared to not return" %
-                      trigger_func)
-        return self.result
+        thread = _TriggerThread(trigger_func, trigger_args)
+        thread.start()
+        try:
+            self.result = block_func(*block_args)
+            # If block_func returned before our thread made the call, we failed!
+            if not thread.startedEvent.is_set():
+                self.fail("blocking function '%r' appeared not to block" %
+                          block_func)
+            return self.result
+        finally:
+            thread.join(10) # make sure the thread terminates
+            if thread.is_alive():
+                self.fail("trigger function '%r' appeared to not return" %
+                          trigger_func)
 
     # Call this instead if block_func is supposed to raise an exception.
     def do_exceptional_blocking_test(self,block_func, block_args, trigger_func,
                                    trigger_args, expected_exception_class):
-        self.t = _TriggerThread(trigger_func, trigger_args)
-        self.t.start()
+        thread = _TriggerThread(trigger_func, trigger_args)
+        thread.start()
         try:
             try:
                 block_func(*block_args)
@@ -77,11 +76,11 @@ class BlockingTestMixin:
                 self.fail("expected exception of kind %r" %
                                  expected_exception_class)
         finally:
-            self.t.join(10) # make sure the thread terminates
-            if self.t.is_alive():
+            thread.join(10) # make sure the thread terminates
+            if thread.is_alive():
                 self.fail("trigger function '%r' appeared to not return" %
                                  trigger_func)
-            if not self.t.startedEvent.is_set():
+            if not thread.startedEvent.is_set():
                 self.fail("trigger thread ended but event never set")
 
 
@@ -159,8 +158,11 @@ class BaseQueueTestMixin(BlockingTestMixin):
 
     def queue_join_test(self, q):
         self.cum = 0
+        threads = []
         for i in (0,1):
-            threading.Thread(target=self.worker, args=(q,)).start()
+            thread = threading.Thread(target=self.worker, args=(q,))
+            thread.start()
+            threads.append(thread)
         for i in range(100):
             q.put(i)
         q.join()
@@ -169,6 +171,8 @@ class BaseQueueTestMixin(BlockingTestMixin):
         for i in (0,1):
             q.put(-1)         # instruct the threads to close
         q.join()                # verify that you can join twice
+        for thread in threads:
+            thread.join()
 
     def test_queue_task_done(self):
         # Test to make sure a queue task completed successfully.
index a6cbbd0..e23e5a9 100644 (file)
@@ -471,7 +471,7 @@ class ReTests(unittest.TestCase):
             m[(0,)]
         with self.assertRaisesRegex(IndexError, 'no such group'):
             m[(0, 1)]
-        with self.assertRaisesRegex(KeyError, 'a2'):
+        with self.assertRaisesRegex(IndexError, 'no such group'):
             'a1={a2}'.format_map(m)
 
         m = pat.match('ac')
index 06a9149..9c97965 100644 (file)
@@ -9,7 +9,7 @@ import subprocess
 import sys
 import tempfile
 import unittest
-from test.support import import_module, unlink, TESTFN
+from test.support import import_module, unlink, temp_dir, TESTFN
 from test.support.script_helper import assert_python_ok
 
 # Skip tests if there is no readline module
@@ -210,13 +210,57 @@ print("history", ascii(readline.get_history_item(1)))
         self.assertIn(b"result " + expected + b"\r\n", output)
         self.assertIn(b"history " + expected + b"\r\n", output)
 
+    # We have 2 reasons to skip this test:
+    # - readline: history size was added in 6.0
+    #   See https://cnswww.cns.cwru.edu/php/chet/readline/CHANGES
+    # - editline: history size is broken on OS X 10.11.6.
+    #   Newer versions were not tested yet.
+    @unittest.skipIf(readline._READLINE_VERSION < 0x600,
+                     "this readline version does not support history-size")
+    @unittest.skipIf(is_editline,
+                     "editline history size configuration is broken")
+    def test_history_size(self):
+        history_size = 10
+        with temp_dir() as test_dir:
+            inputrc = os.path.join(test_dir, "inputrc")
+            with open(inputrc, "wb") as f:
+                f.write(b"set history-size %d\n" % history_size)
+
+            history_file = os.path.join(test_dir, "history")
+            with open(history_file, "wb") as f:
+                # history_size * 2 items crashes readline
+                data = b"".join(b"item %d\n" % i
+                                for i in range(history_size * 2))
+                f.write(data)
+
+            script = """
+import os
+import readline
+
+history_file = os.environ["HISTORY_FILE"]
+readline.read_history_file(history_file)
+input()
+readline.write_history_file(history_file)
+"""
+
+            env = dict(os.environ)
+            env["INPUTRC"] = inputrc
+            env["HISTORY_FILE"] = history_file
+
+            run_pty(script, input=b"last input\r", env=env)
+
+            with open(history_file, "rb") as f:
+                lines = f.readlines()
+            self.assertEqual(len(lines), history_size)
+            self.assertEqual(lines[-1].strip(), b"last input")
+
 
-def run_pty(script, input=b"dummy input\r"):
+def run_pty(script, input=b"dummy input\r", env=None):
     pty = import_module('pty')
     output = bytearray()
     [master, slave] = pty.openpty()
     args = (sys.executable, '-c', script)
-    proc = subprocess.Popen(args, stdin=slave, stdout=slave, stderr=slave)
+    proc = subprocess.Popen(args, stdin=slave, stdout=slave, stderr=slave, env=env)
     os.close(slave)
     with ExitStack() as cleanup:
         cleanup.enter_context(proc)
index 5c6154a..f63ed64 100644 (file)
@@ -191,15 +191,26 @@ class ParseArgsTestCase(unittest.TestCase):
             with self.subTest(opt=opt):
                 ns = libregrtest._parse_args([opt, 'gui,network'])
                 self.assertEqual(ns.use_resources, ['gui', 'network'])
+
                 ns = libregrtest._parse_args([opt, 'gui,none,network'])
                 self.assertEqual(ns.use_resources, ['network'])
-                expected = list(libregrtest.RESOURCE_NAMES)
+
+                expected = list(libregrtest.ALL_RESOURCES)
                 expected.remove('gui')
                 ns = libregrtest._parse_args([opt, 'all,-gui'])
                 self.assertEqual(ns.use_resources, expected)
                 self.checkError([opt], 'expected one argument')
                 self.checkError([opt, 'foo'], 'invalid resource')
 
+                # all + a resource not part of "all"
+                ns = libregrtest._parse_args([opt, 'all,tzdata'])
+                self.assertEqual(ns.use_resources,
+                                 list(libregrtest.ALL_RESOURCES) + ['tzdata'])
+
+                # test another resource which is not part of "all"
+                ns = libregrtest._parse_args([opt, 'extralargefile'])
+                self.assertEqual(ns.use_resources, ['extralargefile'])
+
     def test_memlimit(self):
         for opt in '-M', '--memlimit':
             with self.subTest(opt=opt):
@@ -377,19 +388,19 @@ class BaseTestCase(unittest.TestCase):
         return list(match.group(1) for match in parser)
 
     def check_executed_tests(self, output, tests, skipped=(), failed=(),
-                             omitted=(), randomize=False, interrupted=False):
+                             env_changed=(), omitted=(),
+                             randomize=False, interrupted=False,
+                             fail_env_changed=False):
         if isinstance(tests, str):
             tests = [tests]
         if isinstance(skipped, str):
             skipped = [skipped]
         if isinstance(failed, str):
             failed = [failed]
+        if isinstance(env_changed, str):
+            env_changed = [env_changed]
         if isinstance(omitted, str):
             omitted = [omitted]
-        ntest = len(tests)
-        nskipped = len(skipped)
-        nfailed = len(failed)
-        nomitted = len(omitted)
 
         executed = self.parse_executed_tests(output)
         if randomize:
@@ -415,11 +426,17 @@ class BaseTestCase(unittest.TestCase):
             regex = list_regex('%s test%s failed', failed)
             self.check_line(output, regex)
 
+        if env_changed:
+            regex = list_regex('%s test%s altered the execution environment',
+                               env_changed)
+            self.check_line(output, regex)
+
         if omitted:
             regex = list_regex('%s test%s omitted', omitted)
             self.check_line(output, regex)
 
-        good = ntest - nskipped - nfailed - nomitted
+        good = (len(tests) - len(skipped) - len(failed)
+                - len(omitted) - len(env_changed))
         if good:
             regex = r'%s test%s OK\.$' % (good, plural(good))
             if not skipped and not failed and good > 1:
@@ -429,10 +446,12 @@ class BaseTestCase(unittest.TestCase):
         if interrupted:
             self.check_line(output, 'Test suite interrupted by signal SIGINT.')
 
-        if nfailed:
+        if failed:
             result = 'FAILURE'
         elif interrupted:
             result = 'INTERRUPTED'
+        elif fail_env_changed and env_changed:
+            result = 'ENV CHANGED'
         else:
             result = 'SUCCESS'
         self.check_line(output, 'Tests result: %s' % result)
@@ -604,7 +623,7 @@ class ArgsTestCase(BaseTestCase):
         test_failing = self.create_test('failing', code=code)
         tests = [test_ok, test_failing]
 
-        output = self.run_tests(*tests, exitcode=1)
+        output = self.run_tests(*tests, exitcode=2)
         self.check_executed_tests(output, tests, failed=test_failing)
 
     def test_resources(self):
@@ -703,7 +722,7 @@ class ArgsTestCase(BaseTestCase):
     def test_interrupted(self):
         code = TEST_INTERRUPTED
         test = self.create_test('sigint', code=code)
-        output = self.run_tests(test, exitcode=1)
+        output = self.run_tests(test, exitcode=130)
         self.check_executed_tests(output, test, omitted=test,
                                   interrupted=True)
 
@@ -732,7 +751,7 @@ class ArgsTestCase(BaseTestCase):
                 args = ("--slowest", "-j2", test)
             else:
                 args = ("--slowest", test)
-            output = self.run_tests(*args, exitcode=1)
+            output = self.run_tests(*args, exitcode=130)
             self.check_executed_tests(output, test,
                                       omitted=test, interrupted=True)
 
@@ -772,9 +791,43 @@ class ArgsTestCase(BaseTestCase):
                         builtins.__dict__['RUN'] = 1
         """)
         test = self.create_test('forever', code=code)
-        output = self.run_tests('--forever', test, exitcode=1)
+        output = self.run_tests('--forever', test, exitcode=2)
         self.check_executed_tests(output, [test]*3, failed=test)
 
+    def check_leak(self, code, what):
+        test = self.create_test('huntrleaks', code=code)
+
+        filename = 'reflog.txt'
+        self.addCleanup(support.unlink, filename)
+        output = self.run_tests('--huntrleaks', '3:3:', test,
+                                exitcode=2,
+                                stderr=subprocess.STDOUT)
+        self.check_executed_tests(output, [test], failed=test)
+
+        line = 'beginning 6 repetitions\n123456\n......\n'
+        self.check_line(output, re.escape(line))
+
+        line2 = '%s leaked [1, 1, 1] %s, sum=3\n' % (test, what)
+        self.assertIn(line2, output)
+
+        with open(filename) as fp:
+            reflog = fp.read()
+            self.assertIn(line2, reflog)
+
+    @unittest.skipUnless(Py_DEBUG, 'need a debug build')
+    def test_huntrleaks(self):
+        # test --huntrleaks
+        code = textwrap.dedent("""
+            import unittest
+
+            GLOBAL_LIST = []
+
+            class RefLeakTest(unittest.TestCase):
+                def test_leak(self):
+                    GLOBAL_LIST.append(object())
+        """)
+        self.check_leak(code, 'references')
+
     @unittest.skipUnless(Py_DEBUG, 'need a debug build')
     def test_huntrleaks_fd_leak(self):
         # test --huntrleaks for file descriptor leak
@@ -799,24 +852,7 @@ class ArgsTestCase(BaseTestCase):
                     fd = os.open(__file__, os.O_RDONLY)
                     # bug: never cloes the file descriptor
         """)
-        test = self.create_test('huntrleaks', code=code)
-
-        filename = 'reflog.txt'
-        self.addCleanup(support.unlink, filename)
-        output = self.run_tests('--huntrleaks', '3:3:', test,
-                                exitcode=1,
-                                stderr=subprocess.STDOUT)
-        self.check_executed_tests(output, [test], failed=test)
-
-        line = 'beginning 6 repetitions\n123456\n......\n'
-        self.check_line(output, re.escape(line))
-
-        line2 = '%s leaked [1, 1, 1] file descriptors, sum=3\n' % test
-        self.assertIn(line2, output)
-
-        with open(filename) as fp:
-            reflog = fp.read()
-            self.assertIn(line2, reflog)
+        self.check_leak(code, 'file descriptors')
 
     def test_list_tests(self):
         # test --list-tests
@@ -837,11 +873,20 @@ class ArgsTestCase(BaseTestCase):
                     pass
         """)
         testname = self.create_test(code=code)
+
+        # Test --list-cases
         all_methods = ['%s.Tests.test_method1' % testname,
                        '%s.Tests.test_method2' % testname]
         output = self.run_tests('--list-cases', testname)
         self.assertEqual(output.splitlines(), all_methods)
 
+        # Test --list-cases with --match
+        all_methods = ['%s.Tests.test_method1' % testname]
+        output = self.run_tests('--list-cases',
+                                '-m', 'test_method1',
+                                testname)
+        self.assertEqual(output.splitlines(), all_methods)
+
     def test_crashed(self):
         # Any code which causes a crash
         code = 'import faulthandler; faulthandler._sigsegv()'
@@ -849,7 +894,7 @@ class ArgsTestCase(BaseTestCase):
         ok_test = self.create_test(name="ok")
 
         tests = [crash_test, ok_test]
-        output = self.run_tests("-j2", *tests, exitcode=1)
+        output = self.run_tests("-j2", *tests, exitcode=2)
         self.check_executed_tests(output, tests, failed=crash_test,
                                   randomize=True)
 
@@ -898,6 +943,25 @@ class ArgsTestCase(BaseTestCase):
         subset = ['test_method1', 'test_method3']
         self.assertEqual(methods, subset)
 
+    def test_env_changed(self):
+        code = textwrap.dedent("""
+            import unittest
+
+            class Tests(unittest.TestCase):
+                def test_env_changed(self):
+                    open("env_changed", "w").close()
+        """)
+        testname = self.create_test(code=code)
+
+        # don't fail by default
+        output = self.run_tests(testname)
+        self.check_executed_tests(output, [testname], env_changed=testname)
+
+        # fail with --fail-env-changed
+        output = self.run_tests("--fail-env-changed", testname, exitcode=3)
+        self.check_executed_tests(output, [testname], env_changed=testname,
+                                  fail_env_changed=True)
+
 
 if __name__ == '__main__':
     unittest.main()
index 2ad3a21..b2ab1af 100644 (file)
@@ -1294,7 +1294,7 @@ class TestShutil(unittest.TestCase):
     @unittest.skipUnless(hasattr(shutil, 'disk_usage'),
                          "disk_usage not available on this platform")
     def test_disk_usage(self):
-        usage = shutil.disk_usage(os.getcwd())
+        usage = shutil.disk_usage(os.path.dirname(__file__))
         self.assertGreater(usage.total, 0)
         self.assertGreater(usage.used, 0)
         self.assertGreaterEqual(usage.free, 0)
index ab42ed7..8d63867 100644 (file)
@@ -3,11 +3,13 @@ from test import support
 from contextlib import closing
 import enum
 import gc
+import os
 import pickle
+import random
 import select
 import signal
 import socket
-import struct
+import statistics
 import subprocess
 import traceback
 import sys, os, time, errno
@@ -611,6 +613,15 @@ class ItimerTest(unittest.TestCase):
         # and the handler should have been called
         self.assertEqual(self.hndl_called, True)
 
+    def test_setitimer_tiny(self):
+        # bpo-30807: C setitimer() takes a microsecond-resolution interval.
+        # Check that float -> timeval conversion doesn't round
+        # the interval down to zero, which would disable the timer.
+        self.itimer = signal.ITIMER_REAL
+        signal.setitimer(self.itimer, 1e-6)
+        time.sleep(1)
+        self.assertEqual(self.hndl_called, True)
+
 
 class PendingSignalsTests(unittest.TestCase):
     """
@@ -946,6 +957,135 @@ class PendingSignalsTests(unittest.TestCase):
                                 (exitcode, stdout))
 
 
+class StressTest(unittest.TestCase):
+    """
+    Stress signal delivery, especially when a signal arrives in
+    the middle of recomputing the signal state or executing
+    previously tripped signal handlers.
+    """
+
+    def setsig(self, signum, handler):
+        old_handler = signal.signal(signum, handler)
+        self.addCleanup(signal.signal, signum, old_handler)
+
+    def measure_itimer_resolution(self):
+        N = 20
+        times = []
+
+        def handler(signum=None, frame=None):
+            if len(times) < N:
+                times.append(time.perf_counter())
+                # 1 µs is the smallest possible timer interval,
+                # we want to measure what the concrete duration
+                # will be on this platform
+                signal.setitimer(signal.ITIMER_REAL, 1e-6)
+
+        self.addCleanup(signal.setitimer, signal.ITIMER_REAL, 0)
+        self.setsig(signal.SIGALRM, handler)
+        handler()
+        while len(times) < N:
+            time.sleep(1e-3)
+
+        durations = [times[i+1] - times[i] for i in range(len(times) - 1)]
+        med = statistics.median(durations)
+        if support.verbose:
+            print("detected median itimer() resolution: %.6f s." % (med,))
+        return med
+
+    def decide_itimer_count(self):
+        # Some systems have poor setitimer() resolution (for example
+        # measured around 20 ms. on FreeBSD 9), so decide on a reasonable
+        # number of sequential timers based on that.
+        reso = self.measure_itimer_resolution()
+        if reso <= 1e-4:
+            return 10000
+        elif reso <= 1e-2:
+            return 100
+        else:
+            self.skipTest("detected itimer resolution (%.3f s.) too high "
+                          "(> 10 ms.) on this platform (or system too busy)"
+                          % (reso,))
+
+    @unittest.skipUnless(hasattr(signal, "setitimer"),
+                         "test needs setitimer()")
+    def test_stress_delivery_dependent(self):
+        """
+        This test uses dependent signal handlers.
+        """
+        N = self.decide_itimer_count()
+        sigs = []
+
+        def first_handler(signum, frame):
+            # 1e-6 is the minimum non-zero value for `setitimer()`.
+            # Choose a random delay so as to improve chances of
+            # triggering a race condition.  Ideally the signal is received
+            # when inside critical signal-handling routines such as
+            # Py_MakePendingCalls().
+            signal.setitimer(signal.ITIMER_REAL, 1e-6 + random.random() * 1e-5)
+
+        def second_handler(signum=None, frame=None):
+            sigs.append(signum)
+
+        # Here on Linux, SIGPROF > SIGALRM > SIGUSR1.  By using both
+        # ascending and descending sequences (SIGUSR1 then SIGALRM,
+        # SIGPROF then SIGALRM), we maximize chances of hitting a bug.
+        self.setsig(signal.SIGPROF, first_handler)
+        self.setsig(signal.SIGUSR1, first_handler)
+        self.setsig(signal.SIGALRM, second_handler)  # for ITIMER_REAL
+
+        expected_sigs = 0
+        deadline = time.time() + 15.0
+
+        while expected_sigs < N:
+            os.kill(os.getpid(), signal.SIGPROF)
+            expected_sigs += 1
+            # Wait for handlers to run to avoid signal coalescing
+            while len(sigs) < expected_sigs and time.time() < deadline:
+                time.sleep(1e-5)
+
+            os.kill(os.getpid(), signal.SIGUSR1)
+            expected_sigs += 1
+            while len(sigs) < expected_sigs and time.time() < deadline:
+                time.sleep(1e-5)
+
+        # All ITIMER_REAL signals should have been delivered to the
+        # Python handler
+        self.assertEqual(len(sigs), N, "Some signals were lost")
+
+    @unittest.skipUnless(hasattr(signal, "setitimer"),
+                         "test needs setitimer()")
+    def test_stress_delivery_simultaneous(self):
+        """
+        This test uses simultaneous signal handlers.
+        """
+        N = self.decide_itimer_count()
+        sigs = []
+
+        def handler(signum, frame):
+            sigs.append(signum)
+
+        self.setsig(signal.SIGUSR1, handler)
+        self.setsig(signal.SIGALRM, handler)  # for ITIMER_REAL
+
+        expected_sigs = 0
+        deadline = time.time() + 15.0
+
+        while expected_sigs < N:
+            # Hopefully the SIGALRM will be received somewhere during
+            # initial processing of SIGUSR1.
+            signal.setitimer(signal.ITIMER_REAL, 1e-6 + random.random() * 1e-5)
+            os.kill(os.getpid(), signal.SIGUSR1)
+
+            expected_sigs += 2
+            # Wait for handlers to run to avoid signal coalescing
+            while len(sigs) < expected_sigs and time.time() < deadline:
+                time.sleep(1e-5)
+
+        # All ITIMER_REAL signals should have been delivered to the
+        # Python handler
+        self.assertEqual(len(sigs), N, "Some signals were lost")
+
+
 def tearDownModule():
     support.reap_children()
 
index 28539f3..dd3a6ee 100644 (file)
@@ -604,7 +604,9 @@ class TooLongLineTests(unittest.TestCase):
         self.sock.settimeout(15)
         self.port = support.bind_port(self.sock)
         servargs = (self.evt, self.respdata, self.sock)
-        threading.Thread(target=server, args=servargs).start()
+        thread = threading.Thread(target=server, args=servargs)
+        thread.start()
+        self.addCleanup(thread.join)
         self.evt.wait()
         self.evt.clear()
 
index 85c59a6..54644e1 100644 (file)
@@ -18,6 +18,10 @@ import asyncore
 import weakref
 import platform
 import functools
+try:
+    import ctypes
+except ImportError:
+    ctypes = None
 
 ssl = support.import_module("ssl")
 
@@ -172,6 +176,13 @@ class BasicSocketTests(unittest.TestCase):
             ssl.OP_NO_COMPRESSION
         self.assertIn(ssl.HAS_SNI, {True, False})
         self.assertIn(ssl.HAS_ECDH, {True, False})
+        ssl.OP_NO_SSLv2
+        ssl.OP_NO_SSLv3
+        ssl.OP_NO_TLSv1
+        ssl.OP_NO_TLSv1_3
+    if ssl.OPENSSL_VERSION_INFO >= (1, 0, 1):
+            ssl.OP_NO_TLSv1_1
+            ssl.OP_NO_TLSv1_2
 
     def test_str_for_enums(self):
         # Make sure that the PROTOCOL_* constants have enum-like string
@@ -1736,6 +1747,7 @@ class SimpleBackgroundTests(unittest.TestCase):
         sslobj = ctx.wrap_bio(incoming, outgoing, False, 'localhost')
         self.assertIs(sslobj._sslobj.owner, sslobj)
         self.assertIsNone(sslobj.cipher())
+        self.assertIsNone(sslobj.version())
         self.assertIsNotNone(sslobj.shared_ciphers())
         self.assertRaises(ValueError, sslobj.getpeercert)
         if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES:
@@ -1743,6 +1755,7 @@ class SimpleBackgroundTests(unittest.TestCase):
         self.ssl_io_loop(sock, incoming, outgoing, sslobj.do_handshake)
         self.assertTrue(sslobj.cipher())
         self.assertIsNotNone(sslobj.shared_ciphers())
+        self.assertIsNotNone(sslobj.version())
         self.assertTrue(sslobj.getpeercert())
         if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES:
             self.assertTrue(sslobj.get_channel_binding('tls-unique'))
@@ -1793,34 +1806,6 @@ class NetworkedTests(unittest.TestCase):
             _test_get_server_certificate(self, 'ipv6.google.com', 443)
             _test_get_server_certificate_fail(self, 'ipv6.google.com', 443)
 
-    def test_algorithms(self):
-        # Issue #8484: all algorithms should be available when verifying a
-        # certificate.
-        # SHA256 was added in OpenSSL 0.9.8
-        if ssl.OPENSSL_VERSION_INFO < (0, 9, 8, 0, 15):
-            self.skipTest("SHA256 not available on %r" % ssl.OPENSSL_VERSION)
-        # sha256.tbs-internet.com needs SNI to use the correct certificate
-        if not ssl.HAS_SNI:
-            self.skipTest("SNI needed for this test")
-        # https://sha2.hboeck.de/ was used until 2011-01-08 (no route to host)
-        remote = ("sha256.tbs-internet.com", 443)
-        sha256_cert = os.path.join(os.path.dirname(__file__), "sha256.pem")
-        with support.transient_internet("sha256.tbs-internet.com"):
-            ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
-            ctx.verify_mode = ssl.CERT_REQUIRED
-            ctx.load_verify_locations(sha256_cert)
-            s = ctx.wrap_socket(socket.socket(socket.AF_INET),
-                                server_hostname="sha256.tbs-internet.com")
-            try:
-                s.connect(remote)
-                if support.verbose:
-                    sys.stdout.write("\nCipher with %r is %r\n" %
-                                     (remote, s.cipher()))
-                    sys.stdout.write("Certificate is:\n%s\n" %
-                                     pprint.pformat(s.getpeercert()))
-            finally:
-                s.close()
-
 
 def _test_get_server_certificate(test, host, port, cert=None):
     pem = ssl.get_server_certificate((host, port))
@@ -1871,15 +1856,22 @@ if _have_threads:
                         self.sock, server_side=True)
                     self.server.selected_npn_protocols.append(self.sslconn.selected_npn_protocol())
                     self.server.selected_alpn_protocols.append(self.sslconn.selected_alpn_protocol())
-                except (ssl.SSLError, ConnectionResetError) as e:
+                except (ssl.SSLError, ConnectionResetError, OSError) as e:
                     # We treat ConnectionResetError as though it were an
                     # SSLError - OpenSSL on Ubuntu abruptly closes the
                     # connection when asked to use an unsupported protocol.
                     #
+                    # OSError may occur with wrong protocols, e.g. both
+                    # sides use PROTOCOL_TLS_SERVER.
+                    #
                     # XXX Various errors can have happened here, for example
                     # a mismatching protocol version, an invalid certificate,
                     # or a low-level bug. This should be made more discriminating.
-                    self.server.conn_errors.append(e)
+                    #
+                    # bpo-31323: Store the exception as string to prevent
+                    # a reference leak: server -> conn_errors -> exception
+                    # -> traceback -> self (ConnectionHandler) -> server
+                    self.server.conn_errors.append(str(e))
                     if self.server.chatty:
                         handle_error("\n server:  bad connection attempt from " + repr(self.addr) + ":\n")
                     self.running = False
@@ -2905,7 +2897,13 @@ if _have_threads:
                 s.send(data)
                 buffer = bytearray(len(data))
                 self.assertEqual(s.read(-1, buffer), len(data))
-                self.assertEqual(buffer, data)
+                self.assertEqual(buffer, data)  # sendall accepts bytes-like objects
+
+                if ctypes is not None:
+                    ubyte = ctypes.c_ubyte * len(data)
+                    byteslike = ubyte.from_buffer_copy(data)
+                    s.sendall(byteslike)
+                    self.assertEqual(s.read(), data)
 
                 # Make sure sendmsg et al are disallowed to avoid
                 # inadvertent disclosure of data and/or corruption
@@ -3087,7 +3085,7 @@ if _have_threads:
                 with context.wrap_socket(socket.socket()) as s:
                     with self.assertRaises(OSError):
                         s.connect((HOST, server.port))
-            self.assertIn("no shared cipher", str(server.conn_errors[0]))
+            self.assertIn("no shared cipher", server.conn_errors[0])
 
         def test_version_basic(self):
             """
@@ -3104,12 +3102,33 @@ if _have_threads:
                     self.assertEqual(s.version(), 'TLSv1')
                 self.assertIs(s.version(), None)
 
+        @unittest.skipUnless(ssl.HAS_TLSv1_3,
+                             "test requires TLSv1.3 enabled OpenSSL")
+        def test_tls1_3(self):
+            context = ssl.SSLContext(ssl.PROTOCOL_TLS)
+            context.load_cert_chain(CERTFILE)
+            # disable all but TLS 1.3
+            context.options |= (
+                ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 | ssl.OP_NO_TLSv1_2
+            )
+            with ThreadedEchoServer(context=context) as server:
+                with context.wrap_socket(socket.socket()) as s:
+                    s.connect((HOST, server.port))
+                    self.assertIn(s.cipher()[0], [
+                        'TLS13-AES-256-GCM-SHA384',
+                        'TLS13-CHACHA20-POLY1305-SHA256',
+                        'TLS13-AES-128-GCM-SHA256',
+                    ])
+
         @unittest.skipUnless(ssl.HAS_ECDH, "test requires ECDH-enabled OpenSSL")
         def test_default_ecdh_curve(self):
             # Issue #21015: elliptic curve-based Diffie Hellman key exchange
             # should be enabled by default on SSL contexts.
             context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
             context.load_cert_chain(CERTFILE)
+            # TLSv1.3 defaults to PFS key agreement and no longer has KEA in
+            # cipher name.
+            context.options |= ssl.OP_NO_TLSv1_3
             # Prior to OpenSSL 1.0.0, ECDH ciphers have to be enabled
             # explicitly using the 'ECCdraft' cipher alias.  Otherwise,
             # our default cipher list should prefer ECDH-based ciphers
@@ -3258,8 +3277,9 @@ if _have_threads:
                 except ssl.SSLError as e:
                     stats = e
 
-                if expected is None and IS_OPENSSL_1_1:
-                    # OpenSSL 1.1.0 raises handshake error
+                if (expected is None and IS_OPENSSL_1_1
+                        and ssl.OPENSSL_VERSION_INFO < (1, 1, 0, 6)):
+                    # OpenSSL 1.1.0 to 1.1.0e raises handshake error
                     self.assertIsInstance(stats, ssl.SSLError)
                 else:
                     msg = "failed trying %s (s) and %s (c).\n" \
@@ -3537,6 +3557,10 @@ if _have_threads:
             context2.load_verify_locations(CERTFILE)
             context2.load_cert_chain(CERTFILE)
 
+            # TODO: session reuse does not work with TLS 1.3
+            context.options |= ssl.OP_NO_TLSv1_3
+            context2.options |= ssl.OP_NO_TLSv1_3
+
             server = ThreadedEchoServer(context=context, chatty=False)
             with server:
                 with context.wrap_socket(socket.socket()) as s:
index 9a9e3ca..391d08c 100644 (file)
@@ -21,12 +21,19 @@ try:
     import ctypes
 except ImportError:
     ctypes = None
+else:
+    import ctypes.util
 
 try:
     import threading
 except ImportError:
     threading = None
 
+try:
+    import _testcapi
+except ImportError:
+    _testcapi = None
+
 if support.PGO:
     raise unittest.SkipTest("test is not helpful for PGO")
 
@@ -42,6 +49,8 @@ if mswindows:
 else:
     SETBINARY = ''
 
+NONEXISTING_CMD = ('nonexisting_i_hope',)
+
 
 class BaseTestCase(unittest.TestCase):
     def setUp(self):
@@ -54,6 +63,8 @@ class BaseTestCase(unittest.TestCase):
             inst.wait()
         subprocess._cleanup()
         self.assertFalse(subprocess._active, "subprocess._active not empty")
+        self.doCleanups()
+        support.reap_children()
 
     def assertStderrEqual(self, stderr, expected, msg=None):
         # In a debug build, stuff like "[6580 refs]" is printed to stderr at
@@ -1111,10 +1122,11 @@ class ProcessTestCase(BaseTestCase):
             p.stdin.write(line) # expect that it flushes the line in text mode
             os.close(p.stdin.fileno()) # close it without flushing the buffer
             read_line = p.stdout.readline()
-            try:
-                p.stdin.close()
-            except OSError:
-                pass
+            with support.SuppressCrashReport():
+                try:
+                    p.stdin.close()
+                except OSError:
+                    pass
             p.stdin = None
         self.assertEqual(p.returncode, 0)
         self.assertEqual(read_line, expected)
@@ -1139,13 +1151,54 @@ class ProcessTestCase(BaseTestCase):
         # 1024 times (each call leaked two fds).
         for i in range(1024):
             with self.assertRaises(OSError) as c:
-                subprocess.Popen(['nonexisting_i_hope'],
+                subprocess.Popen(NONEXISTING_CMD,
                                  stdout=subprocess.PIPE,
                                  stderr=subprocess.PIPE)
             # ignore errors that indicate the command was not found
             if c.exception.errno not in (errno.ENOENT, errno.EACCES):
                 raise c.exception
 
+    def test_nonexisting_with_pipes(self):
+        # bpo-30121: Popen with pipes must close properly pipes on error.
+        # Previously, os.close() was called with a Windows handle which is not
+        # a valid file descriptor.
+        #
+        # Run the test in a subprocess to control how the CRT reports errors
+        # and to get stderr content.
+        try:
+            import msvcrt
+            msvcrt.CrtSetReportMode
+        except (AttributeError, ImportError):
+            self.skipTest("need msvcrt.CrtSetReportMode")
+
+        code = textwrap.dedent(f"""
+            import msvcrt
+            import subprocess
+
+            cmd = {NONEXISTING_CMD!r}
+
+            for report_type in [msvcrt.CRT_WARN,
+                                msvcrt.CRT_ERROR,
+                                msvcrt.CRT_ASSERT]:
+                msvcrt.CrtSetReportMode(report_type, msvcrt.CRTDBG_MODE_FILE)
+                msvcrt.CrtSetReportFile(report_type, msvcrt.CRTDBG_FILE_STDERR)
+
+            try:
+                subprocess.Popen([cmd],
+                                 stdout=subprocess.PIPE,
+                                 stderr=subprocess.PIPE)
+            except OSError:
+                pass
+        """)
+        cmd = [sys.executable, "-c", code]
+        proc = subprocess.Popen(cmd,
+                                stderr=subprocess.PIPE,
+                                universal_newlines=True)
+        with proc:
+            stderr = proc.communicate()[1]
+        self.assertEqual(stderr, "")
+        self.assertEqual(proc.returncode, 0)
+
     @unittest.skipIf(threading is None, "threading required")
     def test_double_close_on_error(self):
         # Issue #18851
@@ -1158,7 +1211,7 @@ class ProcessTestCase(BaseTestCase):
         t.start()
         try:
             with self.assertRaises(EnvironmentError):
-                subprocess.Popen(['nonexisting_i_hope'],
+                subprocess.Popen(NONEXISTING_CMD,
                                  stdin=subprocess.PIPE,
                                  stdout=subprocess.PIPE,
                                  stderr=subprocess.PIPE)
@@ -1322,6 +1375,18 @@ class ProcessTestCase(BaseTestCase):
         fds_after_exception = os.listdir(fd_directory)
         self.assertEqual(fds_before_popen, fds_after_exception)
 
+    @unittest.skipIf(mswindows, "behavior currently not supported on Windows")
+    def test_file_not_found_includes_filename(self):
+        with self.assertRaises(FileNotFoundError) as c:
+            subprocess.call(['/opt/nonexistent_binary', 'with', 'some', 'args'])
+        self.assertEqual(c.exception.filename, '/opt/nonexistent_binary')
+
+    @unittest.skipIf(mswindows, "behavior currently not supported on Windows")
+    def test_file_not_found_with_bad_cwd(self):
+        with self.assertRaises(FileNotFoundError) as c:
+            subprocess.Popen(['exit', '0'], cwd='/some/nonexistent/directory')
+        self.assertEqual(c.exception.filename, '/some/nonexistent/directory')
+
 
 class RunFuncTestCase(BaseTestCase):
     def run_python(self, code, **kwargs):
@@ -1480,6 +1545,53 @@ class POSIXProcessTestCase(BaseTestCase):
         else:
             self.fail("Expected OSError: %s" % desired_exception)
 
+    # We mock the __del__ method for Popen in the next two tests
+    # because it does cleanup based on the pid returned by fork_exec
+    # along with issuing a resource warning if it still exists. Since
+    # we don't actually spawn a process in these tests we can forego
+    # the destructor. An alternative would be to set _child_created to
+    # False before the destructor is called but there is no easy way
+    # to do that
+    class PopenNoDestructor(subprocess.Popen):
+        def __del__(self):
+            pass
+
+    @mock.patch("subprocess._posixsubprocess.fork_exec")
+    def test_exception_errpipe_normal(self, fork_exec):
+        """Test error passing done through errpipe_write in the good case"""
+        def proper_error(*args):
+            errpipe_write = args[13]
+            # Write the hex for the error code EISDIR: 'is a directory'
+            err_code = '{:x}'.format(errno.EISDIR).encode()
+            os.write(errpipe_write, b"OSError:" + err_code + b":")
+            return 0
+
+        fork_exec.side_effect = proper_error
+
+        with self.assertRaises(IsADirectoryError):
+            self.PopenNoDestructor(["non_existent_command"])
+
+    @mock.patch("subprocess._posixsubprocess.fork_exec")
+    def test_exception_errpipe_bad_data(self, fork_exec):
+        """Test error passing done through errpipe_write where its not
+        in the expected format"""
+        error_data = b"\xFF\x00\xDE\xAD"
+        def bad_error(*args):
+            errpipe_write = args[13]
+            # Anything can be in the pipe, no assumptions should
+            # be made about its encoding, so we'll write some
+            # arbitrary hex bytes to test it out
+            os.write(errpipe_write, error_data)
+            return 0
+
+        fork_exec.side_effect = bad_error
+
+        with self.assertRaises(subprocess.SubprocessError) as e:
+            self.PopenNoDestructor(["non_existent_command"])
+
+        self.assertIn(repr(error_data), str(e.exception))
+
+
     def test_restore_signals(self):
         # Code coverage for both values of restore_signals to make sure it
         # at least does not blow up.
@@ -2424,7 +2536,7 @@ class POSIXProcessTestCase(BaseTestCase):
         # should trigger the wait() of p
         time.sleep(0.2)
         with self.assertRaises(OSError) as c:
-            with subprocess.Popen(['nonexisting_i_hope'],
+            with subprocess.Popen(NONEXISTING_CMD,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE) as proc:
                 pass
@@ -2565,45 +2677,24 @@ class POSIXProcessTestCase(BaseTestCase):
             proc.communicate(timeout=999)
             mock_proc_stdin.close.assert_called_once_with()
 
-    _libc_file_extensions = {
-      'Linux': 'so.6',
-      'Darwin': 'dylib',
-    }
-    @unittest.skipIf(not ctypes, 'ctypes module required.')
-    @unittest.skipIf(platform.uname()[0] not in _libc_file_extensions,
-                     'Test requires a libc this code can load with ctypes.')
-    @unittest.skipIf(not sys.executable, 'Test requires sys.executable.')
-    def test_child_terminated_in_stopped_state(self):
+    @unittest.skipUnless(_testcapi is not None
+                         and hasattr(_testcapi, 'W_STOPCODE'),
+                         'need _testcapi.W_STOPCODE')
+    def test_stopped(self):
         """Test wait() behavior when waitpid returns WIFSTOPPED; issue29335."""
-        PTRACE_TRACEME = 0  # From glibc and MacOS (PT_TRACE_ME).
-        libc_name = 'libc.' + self._libc_file_extensions[platform.uname()[0]]
-        libc = ctypes.CDLL(libc_name)
-        if not hasattr(libc, 'ptrace'):
-            raise unittest.SkipTest('ptrace() required.')
-        test_ptrace = subprocess.Popen(
-            [sys.executable, '-c', """if True:
-             import ctypes
-             libc = ctypes.CDLL({libc_name!r})
-             libc.ptrace({PTRACE_TRACEME}, 0, 0)
-             """.format(libc_name=libc_name, PTRACE_TRACEME=PTRACE_TRACEME)
-            ])
-        if test_ptrace.wait() != 0:
-            raise unittest.SkipTest('ptrace() failed - unable to test.')
-        child = subprocess.Popen(
-            [sys.executable, '-c', """if True:
-             import ctypes
-             libc = ctypes.CDLL({libc_name!r})
-             libc.ptrace({PTRACE_TRACEME}, 0, 0)
-             libc.printf(ctypes.c_char_p(0xdeadbeef))  # Crash the process.
-             """.format(libc_name=libc_name, PTRACE_TRACEME=PTRACE_TRACEME)
-            ])
-        try:
-            returncode = child.wait()
-        except Exception as e:
-            child.kill()  # Clean up the hung stopped process.
-            raise e
-        self.assertNotEqual(0, returncode)
-        self.assertLess(returncode, 0)  # signal death, likely SIGSEGV.
+        args = [sys.executable, '-c', 'pass']
+        proc = subprocess.Popen(args)
+
+        # Wait until the real process completes to avoid zombie process
+        pid = proc.pid
+        pid, status = os.waitpid(pid, 0)
+        self.assertEqual(status, 0)
+
+        status = _testcapi.W_STOPCODE(3)
+        with mock.patch('subprocess.os.waitpid', return_value=(pid, status)):
+            returncode = proc.wait()
+
+        self.assertEqual(returncode, -3)
 
 
 @unittest.skipUnless(mswindows, "Windows specific tests")
@@ -2644,6 +2735,15 @@ class Win32ProcessTestCase(BaseTestCase):
                           stdout=subprocess.PIPE,
                           close_fds=True)
 
+    @support.cpython_only
+    def test_issue31471(self):
+        # There shouldn't be an assertion failure in Popen() in case the env
+        # argument has a bad keys() method.
+        class BadEnv(dict):
+            keys = None
+        with self.assertRaises(TypeError):
+            subprocess.Popen([sys.executable, "-c", "pass"], env=BadEnv())
+
     def test_close_fds(self):
         # close file descriptors
         rc = subprocess.call([sys.executable, "-c",
@@ -2875,7 +2975,7 @@ class ContextManagerTests(BaseTestCase):
 
     def test_invalid_args(self):
         with self.assertRaises((FileNotFoundError, PermissionError)) as c:
-            with subprocess.Popen(['nonexisting_i_hope'],
+            with subprocess.Popen(NONEXISTING_CMD,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE) as proc:
                 pass
index d0cf04b..710756b 100644 (file)
@@ -78,7 +78,6 @@ class BaseTestCase(unittest.TestCase):
     def tearDown(self):
         self._warnings_manager.__exit__(None, None, None)
 
-
     def nameCheck(self, name, dir, pre, suf):
         (ndir, nbase) = os.path.split(name)
         npre  = nbase[:len(pre)]
@@ -184,12 +183,15 @@ class TestRandomNameSequence(BaseTestCase):
         try:
             pid = os.fork()
             if not pid:
+                # child process
                 os.close(read_fd)
                 os.write(write_fd, next(self.r).encode("ascii"))
                 os.close(write_fd)
                 # bypass the normal exit handlers- leave those to
                 # the parent.
                 os._exit(0)
+
+            # parent process
             parent_value = next(self.r)
             child_value = os.read(read_fd, len(parent_value)).decode("ascii")
         finally:
@@ -200,6 +202,10 @@ class TestRandomNameSequence(BaseTestCase):
                     os.kill(pid, signal.SIGKILL)
                 except OSError:
                     pass
+
+                # Read the process exit status to avoid zombie process
+                os.waitpid(pid, 0)
+
             os.close(read_fd)
             os.close(write_fd)
         self.assertNotEqual(child_value, parent_value)
index 3909b75..2dd1593 100644 (file)
@@ -11,6 +11,7 @@ from test import lock_tests
 
 NUMTASKS = 10
 NUMTRIPS = 3
+POLL_SLEEP = 0.010 # seconds = 10 ms
 
 _print_mutex = thread.allocate_lock()
 
@@ -114,7 +115,7 @@ class ThreadRunningTests(BasicThreadTest):
             mut.release()
         thread.start_new_thread(task, ())
         while not started:
-            time.sleep(0.01)
+            time.sleep(POLL_SLEEP)
         self.assertEqual(thread._count(), orig + 1)
         # Allow the task to finish.
         mut.release()
@@ -125,7 +126,7 @@ class ThreadRunningTests(BasicThreadTest):
         wr = weakref.ref(task, lambda _: done.append(None))
         del task
         while not done:
-            time.sleep(0.01)
+            time.sleep(POLL_SLEEP)
         self.assertEqual(thread._count(), orig)
 
     def test_save_exception_state_on_error(self):
@@ -148,7 +149,7 @@ class ThreadRunningTests(BasicThreadTest):
             thread.start_new_thread(task, ())
             started.acquire()
             while thread._count() > c:
-                time.sleep(0.01)
+                time.sleep(POLL_SLEEP)
         self.assertIn("Traceback", stderr.getvalue())
 
 
@@ -221,28 +222,36 @@ class TestForkInThread(unittest.TestCase):
     def setUp(self):
         self.read_fd, self.write_fd = os.pipe()
 
-    @unittest.skipIf(sys.platform.startswith('win'),
-                     "This test is only appropriate for POSIX-like systems.")
+    @unittest.skipUnless(hasattr(os, 'fork'), 'need os.fork')
     @support.reap_threads
     def test_forkinthread(self):
+        running = True
+        status = "not set"
+
         def thread1():
-            try:
-                pid = os.fork() # fork in a thread
-            except RuntimeError:
-                os._exit(1) # exit the child
+            nonlocal running, status
 
-            if pid == 0: # child
+            # fork in a thread
+            pid = os.fork()
+            if pid == 0:
+                # child
                 try:
                     os.close(self.read_fd)
                     os.write(self.write_fd, b"OK")
                 finally:
                     os._exit(0)
-            else: # parent
+            else:
+                # parent
                 os.close(self.write_fd)
+                pid, status = os.waitpid(pid, 0)
+                running = False
 
         thread.start_new_thread(thread1, ())
         self.assertEqual(os.read(self.read_fd, 2), b"OK",
                          "Unable to fork() in thread")
+        while running:
+            time.sleep(POLL_SLEEP)
+        self.assertEqual(status, 0)
 
     def tearDown(self):
         try:
index 9b2d9a6..f42c900 100644 (file)
@@ -221,7 +221,8 @@ class ThreadedImportTests(unittest.TestCase):
                 import random
             t = threading.Thread(target=target)
             t.start()
-            t.join()"""
+            t.join()
+            t = None"""
         sys.path.insert(0, os.curdir)
         self.addCleanup(sys.path.remove, os.curdir)
         filename = TESTFN + ".py"
@@ -232,6 +233,7 @@ class ThreadedImportTests(unittest.TestCase):
         self.addCleanup(rmtree, '__pycache__')
         importlib.invalidate_caches()
         __import__(TESTFN)
+        del sys.modules[TESTFN]
 
 
 @reap_threads
index 0db0288..9cadc0f 100644 (file)
@@ -575,6 +575,7 @@ class ThreadTests(BaseTestCase):
         self.assertFalse(t.is_alive())
         # And verify the thread disposed of _tstate_lock.
         self.assertIsNone(t._tstate_lock)
+        t.join()
 
     def test_repr_stopped(self):
         # Verify that "stopped" shows up in repr(Thread) appropriately.
@@ -601,6 +602,7 @@ class ThreadTests(BaseTestCase):
                 break
             time.sleep(0.01)
         self.assertIn(LOOKING_FOR, repr(t)) # we waited at least 5 seconds
+        t.join()
 
     def test_BoundedSemaphore_limit(self):
         # BoundedSemaphore should raise ValueError if released too often.
@@ -915,6 +917,7 @@ class ThreadingExceptionTests(BaseTestCase):
         thread = threading.Thread()
         thread.start()
         self.assertRaises(RuntimeError, thread.start)
+        thread.join()
 
     def test_joining_current_thread(self):
         current_thread = threading.current_thread()
@@ -928,6 +931,7 @@ class ThreadingExceptionTests(BaseTestCase):
         thread = threading.Thread()
         thread.start()
         self.assertRaises(RuntimeError, setattr, thread, "daemon", True)
+        thread.join()
 
     def test_releasing_unacquired_lock(self):
         lock = threading.Lock()
@@ -1066,6 +1070,8 @@ class ThreadingExceptionTests(BaseTestCase):
         thread.join()
         self.assertIsNotNone(thread.exc)
         self.assertIsInstance(thread.exc, RuntimeError)
+        # explicitly break the reference cycle to not leak a dangling thread
+        thread.exc = None
 
 class TimerTests(BaseTestCase):
 
@@ -1088,6 +1094,8 @@ class TimerTests(BaseTestCase):
         self.callback_event.wait()
         self.assertEqual(len(self.callback_args), 2)
         self.assertEqual(self.callback_args, [((), {}), ((), {})])
+        timer1.join()
+        timer2.join()
 
     def _callback_spy(self, *args, **kwargs):
         self.callback_args.append((args[:], kwargs.copy()))
index f224212..7093fc6 100644 (file)
@@ -126,6 +126,10 @@ class TimeTestCase(unittest.TestCase):
             except ValueError:
                 self.fail('conversion specifier: %r failed.' % format)
 
+        self.assertRaises(TypeError, time.strftime, b'%S', tt)
+        # embedded null character
+        self.assertRaises(ValueError, time.strftime, '%S\0', tt)
+
     def _bounds_checking(self, func):
         # Make sure that strftime() checks the bounds of the various parts
         # of the time tuple (0 is valid for *all* values).
@@ -485,6 +489,10 @@ class TimeTestCase(unittest.TestCase):
         self.assertRaises(OSError, time.localtime, invalid_time_t)
         self.assertRaises(OSError, time.ctime, invalid_time_t)
 
+        # Issue #26669: check for localtime() failure
+        self.assertRaises(ValueError, time.localtime, float("nan"))
+        self.assertRaises(ValueError, time.ctime, float("nan"))
+
     def test_get_clock_info(self):
         clocks = ['clock', 'perf_counter', 'process_time', 'time']
         if hasattr(time, 'monotonic'):
@@ -819,6 +827,11 @@ class TestCPyTime(CPyTimeTestCase, unittest.TestCase):
                                 lambda secs: secs * SEC_TO_NS,
                                 value_filter=c_int_filter)
 
+        # test nan
+        for time_rnd, _ in ROUNDING_MODES:
+            with self.assertRaises(TypeError):
+                PyTime_FromSeconds(float('nan'))
+
     def test_FromSecondsObject(self):
         from _testcapi import PyTime_FromSecondsObject
 
@@ -830,6 +843,11 @@ class TestCPyTime(CPyTimeTestCase, unittest.TestCase):
             PyTime_FromSecondsObject,
             lambda ns: self.decimal_round(ns * SEC_TO_NS))
 
+        # test nan
+        for time_rnd, _ in ROUNDING_MODES:
+            with self.assertRaises(ValueError):
+                PyTime_FromSecondsObject(float('nan'), time_rnd)
+
     def test_AsSecondsDouble(self):
         from _testcapi import PyTime_AsSecondsDouble
 
@@ -843,6 +861,11 @@ class TestCPyTime(CPyTimeTestCase, unittest.TestCase):
                                 float_converter,
                                 NS_TO_SEC)
 
+        # test nan
+        for time_rnd, _ in ROUNDING_MODES:
+            with self.assertRaises(TypeError):
+                PyTime_AsSecondsDouble(float('nan'))
+
     def create_decimal_converter(self, denominator):
         denom = decimal.Decimal(denominator)
 
@@ -948,6 +971,11 @@ class TestOldPyTime(CPyTimeTestCase, unittest.TestCase):
                                   self.create_converter(SEC_TO_US),
                                   value_filter=self.time_t_filter)
 
+         # test nan
+        for time_rnd, _ in ROUNDING_MODES:
+            with self.assertRaises(ValueError):
+                pytime_object_to_timeval(float('nan'), time_rnd)
+
     def test_object_to_timespec(self):
         from _testcapi import pytime_object_to_timespec
 
@@ -959,6 +987,11 @@ class TestOldPyTime(CPyTimeTestCase, unittest.TestCase):
                                   self.create_converter(SEC_TO_NS),
                                   value_filter=self.time_t_filter)
 
+        # test nan
+        for time_rnd, _ in ROUNDING_MODES:
+            with self.assertRaises(ValueError):
+                pytime_object_to_timespec(float('nan'), time_rnd)
+
 
 if __name__ == "__main__":
     unittest.main()
index 5a81a5f..10e0ad8 100644 (file)
@@ -3,6 +3,7 @@ from tokenize import (tokenize, _tokenize, untokenize, NUMBER, NAME, OP,
                      STRING, ENDMARKER, ENCODING, tok_name, detect_encoding,
                      open as tokenize_open, Untokenizer)
 from io import BytesIO
+import unittest
 from unittest import TestCase, mock
 from test.test_grammar import (VALID_UNDERSCORE_LITERALS,
                                INVALID_UNDERSCORE_LITERALS)
index 7276bc7..e483353 100644 (file)
@@ -344,7 +344,8 @@ class TracebackFormatTests(unittest.TestCase):
         # 2nd last line contains the repetition count
         self.assertEqual(actual[:-2], expected[:-2])
         self.assertRegex(actual[-2], expected[-2])
-        self.assertEqual(actual[-1], expected[-1])
+        # last line can have additional text appended
+        self.assertIn(expected[-1], actual[-1])
 
         # Check the recursion count is roughly as expected
         rec_limit = sys.getrecursionlimit()
index 6d0b265..a3b2ea7 100644 (file)
@@ -3,7 +3,7 @@ import collections
 import pickle
 import re
 import sys
-from unittest import TestCase, main, skipUnless, SkipTest
+from unittest import TestCase, main, skipUnless, SkipTest, expectedFailure
 from copy import copy, deepcopy
 
 from typing import Any, NoReturn
@@ -30,6 +30,13 @@ except ImportError:
     import collections as collections_abc  # Fallback for PY3.2.
 
 
+try:
+    import mod_generics_cache
+except ImportError:
+    # try to use the builtin one, Python 3.5+
+    from test import mod_generics_cache
+
+
 class BaseTestCase(TestCase):
 
     def assertIsSubclass(self, cls, class_or_tuple, msg=None):
@@ -836,10 +843,6 @@ class GenericTests(BaseTestCase):
         self.assertEqual(Callable[..., GenericMeta].__args__, (Ellipsis, GenericMeta))
 
     def test_generic_hashes(self):
-        try:
-            from test import mod_generics_cache
-        except ImportError:  # for Python 3.4 and previous versions
-            import mod_generics_cache
         class A(Generic[T]):
             ...
 
@@ -1069,6 +1072,13 @@ class GenericTests(BaseTestCase):
         for t in things + [Any]:
             self.assertEqual(t, copy(t))
             self.assertEqual(t, deepcopy(t))
+            if sys.version_info >= (3, 3):
+                # From copy module documentation:
+                # It does "copy" functions and classes (shallow and deeply), by returning
+                # the original object unchanged; this is compatible with the way these
+                # are treated by the pickle module.
+                self.assertTrue(t is copy(t))
+                self.assertTrue(t is deepcopy(t))
 
     def test_weakref_all(self):
         T = TypeVar('T')
@@ -1612,6 +1622,10 @@ class XRepr(NamedTuple):
     def __add__(self, other):
         return 0
 
+class HasForeignBaseClass(mod_generics_cache.A):
+    some_xrepr: 'XRepr'
+    other_a: 'mod_generics_cache.A'
+
 async def g_with(am: AsyncContextManager[int]):
     x: int
     async with am as x:
@@ -1652,8 +1666,18 @@ class GetTypeHintTests(BaseTestCase):
         self.assertEqual(gth(ann_module3), {})
 
     @skipUnless(PY36, 'Python 3.6 required')
+    @expectedFailure
+    def test_get_type_hints_modules_forwardref(self):
+        # FIXME: This currently exposes a bug in typing. Cached forward references
+        # don't account for the case where there are multiple types of the same
+        # name coming from different modules in the same program.
+        mgc_hints = {'default_a': Optional[mod_generics_cache.A],
+                     'default_b': Optional[mod_generics_cache.B]}
+        self.assertEqual(gth(mod_generics_cache), mgc_hints)
+
+    @skipUnless(PY36, 'Python 3.6 required')
     def test_get_type_hints_classes(self):
-        self.assertEqual(gth(ann_module.C, ann_module.__dict__),
+        self.assertEqual(gth(ann_module.C),  # gth will find the right globalns
                          {'y': Optional[ann_module.C]})
         self.assertIsInstance(gth(ann_module.j_class), dict)
         self.assertEqual(gth(ann_module.M), {'123': 123, 'o': type})
@@ -1664,8 +1688,15 @@ class GetTypeHintTests(BaseTestCase):
                          {'y': Optional[ann_module.C]})
         self.assertEqual(gth(ann_module.S), {'x': str, 'y': str})
         self.assertEqual(gth(ann_module.foo), {'x': int})
-        self.assertEqual(gth(NoneAndForward, globals()),
+        self.assertEqual(gth(NoneAndForward),
                          {'parent': NoneAndForward, 'meaning': type(None)})
+        self.assertEqual(gth(HasForeignBaseClass),
+                         {'some_xrepr': XRepr, 'other_a': mod_generics_cache.A,
+                          'some_b': mod_generics_cache.B})
+        self.assertEqual(gth(mod_generics_cache.B),
+                         {'my_inner_a1': mod_generics_cache.B.A,
+                          'my_inner_a2': mod_generics_cache.B.A,
+                          'my_outer_a': mod_generics_cache.A})
 
     @skipUnless(PY36, 'Python 3.6 required')
     def test_respect_no_type_check(self):
index 2844bc5..56ab0ad 100644 (file)
@@ -1279,6 +1279,13 @@ class UnicodeTest(string_tests.CommonTest,
         self.assertRaises(ValueError, '{}'.format_map, 'a')
         self.assertRaises(ValueError, '{a} {}'.format_map, {"a" : 2, "b" : 1})
 
+        class BadMapping:
+            def __getitem__(self, key):
+                return 1/0
+        self.assertRaises(KeyError, '{a}'.format_map, {})
+        self.assertRaises(TypeError, '{a}'.format_map, [])
+        self.assertRaises(ZeroDivisionError, '{a}'.format_map, BadMapping())
+
     def test_format_huge_precision(self):
         format_string = ".{}f".format(sys.maxsize + 1)
         with self.assertRaises(ValueError):
index 70c4c01..f83f9cc 100644 (file)
@@ -289,11 +289,15 @@ class BasicAuthTests(unittest.TestCase):
         def http_server_with_basic_auth_handler(*args, **kwargs):
             return BasicAuthHandler(*args, **kwargs)
         self.server = LoopbackHttpServerThread(http_server_with_basic_auth_handler)
-        self.addCleanup(self.server.stop)
+        self.addCleanup(self.stop_server)
         self.server_url = 'http://127.0.0.1:%s' % self.server.port
         self.server.start()
         self.server.ready.wait()
 
+    def stop_server(self):
+        self.server.stop()
+        self.server = None
+
     def tearDown(self):
         super(BasicAuthTests, self).tearDown()
 
@@ -339,6 +343,7 @@ class ProxyAuthTests(unittest.TestCase):
             return FakeProxyHandler(self.digest_auth_handler, *args, **kwargs)
 
         self.server = LoopbackHttpServerThread(create_fake_proxy_handler)
+        self.addCleanup(self.stop_server)
         self.server.start()
         self.server.ready.wait()
         proxy_url = "http://127.0.0.1:%d" % self.server.port
@@ -347,9 +352,9 @@ class ProxyAuthTests(unittest.TestCase):
         self.opener = urllib.request.build_opener(
             handler, self.proxy_digest_handler)
 
-    def tearDown(self):
+    def stop_server(self):
         self.server.stop()
-        super(ProxyAuthTests, self).tearDown()
+        self.server = None
 
     def test_proxy_with_bad_password_raises_httperror(self):
         self.proxy_digest_handler.add_password(self.REALM, self.URL,
@@ -468,13 +473,17 @@ class TestUrlopen(unittest.TestCase):
             f.close()
         return b"".join(l)
 
+    def stop_server(self):
+        self.server.stop()
+        self.server = None
+
     def start_server(self, responses=None):
         if responses is None:
             responses = [(200, [], b"we don't care")]
         handler = GetRequestHandler(responses)
 
         self.server = LoopbackHttpServerThread(handler)
-        self.addCleanup(self.server.stop)
+        self.addCleanup(self.stop_server)
         self.server.start()
         self.server.ready.wait()
         port = self.server.port
@@ -664,7 +673,7 @@ def setUpModule():
 
 def tearDownModule():
     if threads_key:
-        support.threading_cleanup(threads_key)
+        support.threading_cleanup(*threads_key)
 
 if __name__ == "__main__":
     unittest.main()
index cad83fd..13e2dd5 100644 (file)
@@ -97,8 +97,8 @@ class OtherNetworkTests(unittest.TestCase):
 
     def test_ftp(self):
         urls = [
-            'ftp://ftp.debian.org/debian/README',
-            ('ftp://ftp.debian.org/debian/non-existent-file',
+            'ftp://www.pythontest.net/README',
+            ('ftp://www.pythontest.net/non-existent-file',
              None, urllib.error.URLError),
             ]
         self._test_urls(urls, self._extra_handlers())
@@ -287,7 +287,7 @@ class TimeoutTest(unittest.TestCase):
             self.addCleanup(u.close)
             self.assertEqual(u.fp.raw._sock.gettimeout(), 120)
 
-    FTP_HOST = 'ftp://ftp.debian.org/debian/'
+    FTP_HOST = 'ftp://www.pythontest.net/'
 
     def test_ftp_basic(self):
         self.assertIsNone(socket.getdefaulttimeout())
index 0cddf4a..354da6b 100644 (file)
@@ -727,10 +727,15 @@ class _WarningsTests(BaseTest, unittest.TestCase):
         text = 'del _showwarnmsg test'
         with original_warnings.catch_warnings(module=self.module):
             self.module.filterwarnings("always", category=UserWarning)
-            del self.module._showwarnmsg
-            with support.captured_output('stderr') as stream:
-                self.module.warn(text)
-                result = stream.getvalue()
+
+            show = self.module._showwarnmsg
+            try:
+                del self.module._showwarnmsg
+                with support.captured_output('stderr') as stream:
+                    self.module.warn(text)
+                    result = stream.getvalue()
+            finally:
+                self.module._showwarnmsg = show
         self.assertIn(text, result)
 
     def test_showwarning_not_callable(self):
@@ -789,6 +794,32 @@ class _WarningsTests(BaseTest, unittest.TestCase):
         self.assertNotIn(b'Warning!', stderr)
         self.assertNotIn(b'Error', stderr)
 
+    @support.cpython_only
+    def test_issue31411(self):
+        # warn_explicit() shouldn't raise a SystemError in case
+        # warnings.onceregistry isn't a dictionary.
+        wmod = self.module
+        with original_warnings.catch_warnings(module=wmod):
+            wmod.filterwarnings('once')
+            with support.swap_attr(wmod, 'onceregistry', None):
+                with self.assertRaises(TypeError):
+                    wmod.warn_explicit('foo', Warning, 'bar', 1, registry=None)
+
+    @support.cpython_only
+    def test_issue31416(self):
+        # warn_explicit() shouldn't cause an assertion failure in case of a
+        # bad warnings.filters or warnings.defaultaction.
+        wmod = self.module
+        with original_warnings.catch_warnings(module=wmod):
+            wmod.filters = [(None, None, Warning, None, 0)]
+            with self.assertRaises(TypeError):
+                wmod.warn_explicit('foo', Warning, 'bar', 1)
+
+            wmod.filters = []
+            with support.swap_attr(wmod, 'defaultaction', None), \
+                 self.assertRaises(TypeError):
+                wmod.warn_explicit('foo', Warning, 'bar', 1)
+
 
 class WarningsDisplayTests(BaseTest):
 
index 179e069..21437ef 100644 (file)
@@ -98,6 +98,8 @@ class PlaySoundTest(unittest.TestCase):
         self.assertRaises(TypeError, winsound.PlaySound, "bad",
                           winsound.SND_MEMORY)
         self.assertRaises(TypeError, winsound.PlaySound, 1, 0)
+        # embedded null character
+        self.assertRaises(ValueError, winsound.PlaySound, 'bad\0', 0)
 
     def test_keyword_args(self):
         safe_PlaySound(flags=winsound.SND_ALIAS, sound="SystemExit")
index dbdad23..ad0ffb3 100644 (file)
@@ -1523,6 +1523,7 @@ class BugsTest(unittest.TestCase):
         self.assertEqual(t.find('.//paragraph').text,
             'A new cultivar of Begonia plant named \u2018BCT9801BEG\u2019.')
 
+    @unittest.skipIf(sys.gettrace(), "Skips under coverage.")
     def test_bug_xmltoolkit63(self):
         # Check reference leak.
         def xmltoolkit63():
@@ -2475,6 +2476,31 @@ class TreeBuilderTest(unittest.TestCase):
             ('html', '-//W3C//DTD XHTML 1.0 Transitional//EN',
              'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'))
 
+    def test_builder_lookup_errors(self):
+        class RaisingBuilder:
+            def __init__(self, raise_in=None, what=ValueError):
+                self.raise_in = raise_in
+                self.what = what
+
+            def __getattr__(self, name):
+                if name == self.raise_in:
+                    raise self.what(self.raise_in)
+                def handle(*args):
+                    pass
+                return handle
+
+        ET.XMLParser(target=RaisingBuilder())
+        # cET also checks for 'close' and 'doctype', PyET does it only at need
+        for event in ('start', 'data', 'end', 'comment', 'pi'):
+            with self.assertRaisesRegex(ValueError, event):
+                ET.XMLParser(target=RaisingBuilder(event))
+
+        ET.XMLParser(target=RaisingBuilder(what=AttributeError))
+        for event in ('start', 'data', 'end', 'comment', 'pi'):
+            parser = ET.XMLParser(target=RaisingBuilder(event, what=AttributeError))
+            parser.feed(self.sample1)
+            self.assertIsNone(parser.close())
+
 
 class XMLParserTest(unittest.TestCase):
     sample1 = b'<file><line>22</line></file>'
index 7c60699..b2200d3 100644 (file)
@@ -64,6 +64,26 @@ class MiscTests(unittest.TestCase):
         del root
         support.gc_collect()
 
+    def test_parser_ref_cycle(self):
+        # bpo-31499: xmlparser_dealloc() crashed with a segmentation fault when
+        # xmlparser_gc_clear() was called previously by the garbage collector,
+        # when the parser was part of a reference cycle.
+
+        def parser_ref_cycle():
+            parser = cET.XMLParser()
+            # Create a reference cycle using an exception to keep the frame
+            # alive, so the parser will be destroyed by the garbage collector
+            try:
+                raise ValueError
+            except ValueError as exc:
+                err = exc
+
+        # Create a parser part of reference cycle
+        parser_ref_cycle()
+        # Trigger an explicit garbage collection to break the reference cycle
+        # and so destroy the parser
+        support.gc_collect()
+
 
 @unittest.skipUnless(cET, 'requires _elementtree')
 class TestAliasWorking(unittest.TestCase):
index 30025e3..f9bf304 100644 (file)
@@ -328,6 +328,10 @@ class XMLRPCTestCase(unittest.TestCase):
                 self.handled = True
                 self.close_connection = False
 
+            def log_message(self, format, *args):
+                # don't clobber sys.stderr
+                pass
+
         def run_server():
             server.socket.settimeout(float(1))  # Don't hang if client fails
             server.handle_request()  # First request and attempt at second
@@ -755,7 +759,9 @@ class BaseServerTestCase(unittest.TestCase):
         self.evt = threading.Event()
         # start server thread to handle requests
         serv_args = (self.evt, self.request_count, self.requestHandler)
-        threading.Thread(target=self.threadFunc, args=serv_args).start()
+        thread = threading.Thread(target=self.threadFunc, args=serv_args)
+        thread.start()
+        self.addCleanup(thread.join)
 
         # wait for the server to be ready
         self.evt.wait()
@@ -1207,7 +1213,9 @@ class FailingServerTestCase(unittest.TestCase):
         self.evt = threading.Event()
         # start server thread to handle requests
         serv_args = (self.evt, 1)
-        threading.Thread(target=http_server, args=serv_args).start()
+        thread = threading.Thread(target=http_server, args=serv_args)
+        thread.start()
+        self.addCleanup(thread.join)
 
         # wait for the server to be ready
         self.evt.wait()
index 7ddbc50..daa5138 100644 (file)
@@ -521,6 +521,23 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase):
             z.close()
             os.remove(TEMP_ZIP)
 
+    def test_issue31291(self):
+        # There shouldn't be an assertion failure in get_data().
+        class FunnyStr(str):
+            def replace(self, old, new):
+                return 42
+        z = ZipFile(TEMP_ZIP, "w")
+        try:
+            name = "test31291.dat"
+            data = b'foo'
+            z.writestr(name, data)
+            z.close()
+            zi = zipimport.zipimporter(TEMP_ZIP)
+            self.assertEqual(data, zi.get_data(FunnyStr(name)))
+        finally:
+            z.close()
+            os.remove(TEMP_ZIP)
+
     def testImporterAttr(self):
         src = """if 1:  # indent hack
         def get_file():
index 218b27f..a45f882 100644 (file)
@@ -291,6 +291,31 @@ class OptionMenuTest(AbstractTkTest, unittest.TestCase):
 
         optmenu.destroy()
 
+    def test_unique_radiobuttons(self):
+        # check that radiobuttons are unique across instances (bpo25684)
+        items = ('a', 'b', 'c')
+        default = 'a'
+        optmenu = ttk.OptionMenu(self.root, self.textvar, default, *items)
+        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')
+        optmenu2_stringvar_name = optmenu2['menu'].entrycget(0, 'variable')
+        self.assertNotEqual(optmenu_stringvar_name,
+                            optmenu2_stringvar_name)
+        self.assertEqual(self.root.tk.globalgetvar(optmenu_stringvar_name),
+                         items[1])
+        self.assertEqual(self.root.tk.globalgetvar(optmenu2_stringvar_name),
+                         items[2])
+
+        optmenu.destroy()
+        optmenu2.destroy()
+
 
 tests_gui = (LabeledScaleTest, OptionMenuTest)
 
index c474e60..2a3e1ce 100644 (file)
@@ -1543,11 +1543,12 @@ class LabeledScale(Frame):
         try:
             self._variable.trace_vdelete('w', self.__tracecb)
         except AttributeError:
-            # widget has been destroyed already
             pass
         else:
             del self._variable
-            Frame.destroy(self)
+        super().destroy()
+        self.label = None
+        self.scale = None
 
 
     def _adjust(self, *args):
@@ -1638,7 +1639,8 @@ class OptionMenu(Menubutton):
         menu.delete(0, 'end')
         for val in values:
             menu.add_radiobutton(label=val,
-                command=tkinter._setit(self._variable, val, self._callback))
+                command=tkinter._setit(self._variable, val, self._callback),
+                variable=self._variable)
 
         if default:
             self._variable.set(default)
@@ -1646,5 +1648,8 @@ class OptionMenu(Menubutton):
 
     def destroy(self):
         """Destroy this widget and its associated variable."""
-        del self._variable
-        Menubutton.destroy(self)
+        try:
+            del self._variable
+        except AttributeError:
+            pass
+        super().destroy()
index c487afc..c00a3a1 100644 (file)
@@ -376,7 +376,7 @@ def _type_check(arg, msg):
     if (
         type(arg).__name__ in ('_Union', '_Optional') and
         not getattr(arg, '__origin__', None) or
-        isinstance(arg, TypingMeta) and _gorg(arg) in (Generic, _Protocol)
+        isinstance(arg, TypingMeta) and arg._gorg in (Generic, _Protocol)
     ):
         raise TypeError("Plain %s is not valid as type argument" % arg)
     return arg
@@ -849,29 +849,6 @@ class _Optional(_FinalTypingBase, _root=True):
 Optional = _Optional(_root=True)
 
 
-def _gorg(a):
-    """Return the farthest origin of a generic class (internal helper)."""
-    assert isinstance(a, GenericMeta)
-    while a.__origin__ is not None:
-        a = a.__origin__
-    return a
-
-
-def _geqv(a, b):
-    """Return whether two generic classes are equivalent (internal helper).
-
-    The intention is to consider generic class X and any of its
-    parameterized forms (X[T], X[int], etc.) as equivalent.
-
-    However, X is not equivalent to a subclass of X.
-
-    The relation is reflexive, symmetric and transitive.
-    """
-    assert isinstance(a, GenericMeta) and isinstance(b, GenericMeta)
-    # Reduce each to its origin.
-    return _gorg(a) is _gorg(b)
-
-
 def _next_in_mro(cls):
     """Helper for Generic.__new__.
 
@@ -881,7 +858,7 @@ def _next_in_mro(cls):
     next_in_mro = object
     # Look for the last occurrence of Generic or Generic[...].
     for i, c in enumerate(cls.__mro__[:-1]):
-        if isinstance(c, GenericMeta) and _gorg(c) is Generic:
+        if isinstance(c, GenericMeta) and c._gorg is Generic:
             next_in_mro = cls.__mro__[i + 1]
     return next_in_mro
 
@@ -991,14 +968,15 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
         initial_bases = bases
         if extra is not None and type(extra) is abc.ABCMeta and extra not in bases:
             bases = (extra,) + bases
-        bases = tuple(_gorg(b) if isinstance(b, GenericMeta) else b for b in bases)
+        bases = tuple(b._gorg if isinstance(b, GenericMeta) else b for b in bases)
 
         # remove bare Generic from bases if there are other generic bases
         if any(isinstance(b, GenericMeta) and b is not Generic for b in bases):
             bases = tuple(b for b in bases if b is not Generic)
         namespace.update({'__origin__': origin, '__extra__': extra})
         self = super().__new__(cls, name, bases, namespace, _root=True)
-
+        super(GenericMeta, self).__setattr__('_gorg',
+                                             self if not origin else origin._gorg)
         self.__parameters__ = tvars
         # Be prepared that GenericMeta will be subclassed by TupleMeta
         # and CallableMeta, those two allow ..., (), or [] in __args___.
@@ -1041,7 +1019,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
     def _abc_negative_cache(self):
         if isinstance(self.__extra__, abc.ABCMeta):
             return self.__extra__._abc_negative_cache
-        return _gorg(self)._abc_generic_negative_cache
+        return self._gorg._abc_generic_negative_cache
 
     @_abc_negative_cache.setter
     def _abc_negative_cache(self, value):
@@ -1055,7 +1033,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
     def _abc_negative_cache_version(self):
         if isinstance(self.__extra__, abc.ABCMeta):
             return self.__extra__._abc_negative_cache_version
-        return _gorg(self)._abc_generic_negative_cache_version
+        return self._gorg._abc_generic_negative_cache_version
 
     @_abc_negative_cache_version.setter
     def _abc_negative_cache_version(self, value):
@@ -1105,7 +1083,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
         if self.__origin__ is None:
             return self
         tree_args = _subs_tree(self, tvars, args)
-        return (_gorg(self),) + tuple(tree_args)
+        return (self._gorg,) + tuple(tree_args)
 
     def __eq__(self, other):
         if not isinstance(other, GenericMeta):
@@ -1121,7 +1099,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
     def __getitem__(self, params):
         if not isinstance(params, tuple):
             params = (params,)
-        if not params and not _gorg(self) is Tuple:
+        if not params and self._gorg is not Tuple:
             raise TypeError(
                 "Parameter list to %s[...] cannot be empty" % _qualname(self))
         msg = "Parameters to generic types must be types."
@@ -1189,14 +1167,14 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
                               self.__extra__, self.__orig_bases__)
 
     def __setattr__(self, attr, value):
-        # We consider all the subscripted genrics as proxies for original class
+        # We consider all the subscripted generics as proxies for original class
         if (
             attr.startswith('__') and attr.endswith('__') or
             attr.startswith('_abc_')
         ):
             super(GenericMeta, self).__setattr__(attr, value)
         else:
-            super(GenericMeta, _gorg(self)).__setattr__(attr, value)
+            super(GenericMeta, self._gorg).__setattr__(attr, value)
 
 
 # Prevent checks for Generic to crash when defining Generic.
@@ -1209,7 +1187,7 @@ def _generic_new(base_cls, cls, *args, **kwds):
     if cls.__origin__ is None:
         return base_cls.__new__(cls)
     else:
-        origin = _gorg(cls)
+        origin = cls._gorg
         obj = base_cls.__new__(origin)
         try:
             obj.__orig_class__ = cls
@@ -1243,7 +1221,7 @@ class Generic(metaclass=GenericMeta):
     __slots__ = ()
 
     def __new__(cls, *args, **kwds):
-        if _geqv(cls, Generic):
+        if cls._gorg is Generic:
             raise TypeError("Type Generic cannot be instantiated; "
                             "it can be used only as a base class")
         return _generic_new(cls.__next_in_mro__, cls, *args, **kwds)
@@ -1265,7 +1243,7 @@ class TupleMeta(GenericMeta):
 
     @_tp_cache
     def __getitem__(self, parameters):
-        if self.__origin__ is not None or not _geqv(self, Tuple):
+        if self.__origin__ is not None or self._gorg is not Tuple:
             # Normal generic rules apply if this is not the first subscription
             # or a subscription of a subclass.
             return super().__getitem__(parameters)
@@ -1307,7 +1285,7 @@ class Tuple(tuple, extra=tuple, metaclass=TupleMeta):
     __slots__ = ()
 
     def __new__(cls, *args, **kwds):
-        if _geqv(cls, Tuple):
+        if cls._gorg is Tuple:
             raise TypeError("Type Tuple cannot be instantiated; "
                             "use tuple() instead")
         return _generic_new(tuple, cls, *args, **kwds)
@@ -1322,7 +1300,7 @@ class CallableMeta(GenericMeta):
         return self._tree_repr(self._subs_tree())
 
     def _tree_repr(self, tree):
-        if _gorg(self) is not Callable:
+        if self._gorg is not Callable:
             return super()._tree_repr(tree)
         # For actual Callable (not its subclass) we override
         # super()._tree_repr() for nice formatting.
@@ -1342,7 +1320,7 @@ class CallableMeta(GenericMeta):
         with hashable arguments to improve speed.
         """
 
-        if self.__origin__ is not None or not _geqv(self, Callable):
+        if self.__origin__ is not None or self._gorg is not Callable:
             return super().__getitem__(parameters)
         if not isinstance(parameters, tuple) or len(parameters) != 2:
             raise TypeError("Callable must be used as "
@@ -1384,7 +1362,7 @@ class Callable(extra=collections_abc.Callable, metaclass=CallableMeta):
     __slots__ = ()
 
     def __new__(cls, *args, **kwds):
-        if _geqv(cls, Callable):
+        if cls._gorg is Callable:
             raise TypeError("Type Callable cannot be instantiated; "
                             "use a non-abstract subclass instead")
         return _generic_new(cls.__next_in_mro__, cls, *args, **kwds)
@@ -1503,8 +1481,9 @@ def get_type_hints(obj, globalns=None, localns=None):
     search order is locals first, then globals.
 
     - If no dict arguments are passed, an attempt is made to use the
-      globals from obj, and these are also used as the locals.  If the
-      object does not appear to have globals, an exception is raised.
+      globals from obj (or the respective module's globals for classes),
+      and these are also used as the locals.  If the object does not appear
+      to have globals, an empty dictionary is used.
 
     - If one dict argument is passed, it is used for both globals and
       locals.
@@ -1515,25 +1494,33 @@ def get_type_hints(obj, globalns=None, localns=None):
 
     if getattr(obj, '__no_type_check__', None):
         return {}
-    if globalns is None:
-        globalns = getattr(obj, '__globals__', {})
-        if localns is None:
-            localns = globalns
-    elif localns is None:
-        localns = globalns
     # Classes require a special treatment.
     if isinstance(obj, type):
         hints = {}
         for base in reversed(obj.__mro__):
+            if globalns is None:
+                base_globals = sys.modules[base.__module__].__dict__
+            else:
+                base_globals = globalns
             ann = base.__dict__.get('__annotations__', {})
             for name, value in ann.items():
                 if value is None:
                     value = type(None)
                 if isinstance(value, str):
                     value = _ForwardRef(value)
-                value = _eval_type(value, globalns, localns)
+                value = _eval_type(value, base_globals, localns)
                 hints[name] = value
         return hints
+
+    if globalns is None:
+        if isinstance(obj, types.ModuleType):
+            globalns = obj.__dict__
+        else:
+            globalns = getattr(obj, '__globals__', {})
+        if localns is None:
+            localns = globalns
+    elif localns is None:
+        localns = globalns
     hints = getattr(obj, '__annotations__', None)
     if hints is None:
         # Return empty annotations for something that _could_ have them.
@@ -1568,7 +1555,7 @@ def no_type_check(arg):
     if isinstance(arg, type):
         arg_attrs = arg.__dict__.copy()
         for attr, val in arg.__dict__.items():
-            if val in arg.__bases__:
+            if val in arg.__bases__ + (arg,):
                 arg_attrs.pop(attr)
         for obj in arg_attrs.values():
             if isinstance(obj, types.FunctionType):
@@ -1687,6 +1674,7 @@ class _ProtocolMeta(GenericMeta):
                             attr != '__annotations__' and
                             attr != '__weakref__' and
                             attr != '_is_protocol' and
+                            attr != '_gorg' and
                             attr != '__dict__' and
                             attr != '__args__' and
                             attr != '__slots__' and
@@ -1892,7 +1880,7 @@ class List(list, MutableSequence[T], extra=list):
     __slots__ = ()
 
     def __new__(cls, *args, **kwds):
-        if _geqv(cls, List):
+        if cls._gorg is List:
             raise TypeError("Type List cannot be instantiated; "
                             "use list() instead")
         return _generic_new(list, cls, *args, **kwds)
@@ -1903,7 +1891,7 @@ class Deque(collections.deque, MutableSequence[T], extra=collections.deque):
     __slots__ = ()
 
     def __new__(cls, *args, **kwds):
-        if _geqv(cls, Deque):
+        if cls._gorg is Deque:
             return collections.deque(*args, **kwds)
         return _generic_new(collections.deque, cls, *args, **kwds)
 
@@ -1913,7 +1901,7 @@ class Set(set, MutableSet[T], extra=set):
     __slots__ = ()
 
     def __new__(cls, *args, **kwds):
-        if _geqv(cls, Set):
+        if cls._gorg is Set:
             raise TypeError("Type Set cannot be instantiated; "
                             "use set() instead")
         return _generic_new(set, cls, *args, **kwds)
@@ -1923,7 +1911,7 @@ class FrozenSet(frozenset, AbstractSet[T_co], extra=frozenset):
     __slots__ = ()
 
     def __new__(cls, *args, **kwds):
-        if _geqv(cls, FrozenSet):
+        if cls._gorg is FrozenSet:
             raise TypeError("Type FrozenSet cannot be instantiated; "
                             "use frozenset() instead")
         return _generic_new(frozenset, cls, *args, **kwds)
@@ -2014,7 +2002,7 @@ class Dict(dict, MutableMapping[KT, VT], extra=dict):
     __slots__ = ()
 
     def __new__(cls, *args, **kwds):
-        if _geqv(cls, Dict):
+        if cls._gorg is Dict:
             raise TypeError("Type Dict cannot be instantiated; "
                             "use dict() instead")
         return _generic_new(dict, cls, *args, **kwds)
@@ -2026,7 +2014,7 @@ class DefaultDict(collections.defaultdict, MutableMapping[KT, VT],
     __slots__ = ()
 
     def __new__(cls, *args, **kwds):
-        if _geqv(cls, DefaultDict):
+        if cls._gorg is DefaultDict:
             return collections.defaultdict(*args, **kwds)
         return _generic_new(collections.defaultdict, cls, *args, **kwds)
 
@@ -2036,7 +2024,7 @@ class Counter(collections.Counter, Dict[T, int], extra=collections.Counter):
     __slots__ = ()
 
     def __new__(cls, *args, **kwds):
-        if _geqv(cls, Counter):
+        if cls._gorg is Counter:
             return collections.Counter(*args, **kwds)
         return _generic_new(collections.Counter, cls, *args, **kwds)
 
@@ -2051,7 +2039,7 @@ if hasattr(collections, 'ChainMap'):
         __slots__ = ()
 
         def __new__(cls, *args, **kwds):
-            if _geqv(cls, ChainMap):
+            if cls._gorg is ChainMap:
                 return collections.ChainMap(*args, **kwds)
             return _generic_new(collections.ChainMap, cls, *args, **kwds)
 
@@ -2070,7 +2058,7 @@ class Generator(Iterator[T_co], Generic[T_co, T_contra, V_co],
     __slots__ = ()
 
     def __new__(cls, *args, **kwds):
-        if _geqv(cls, Generator):
+        if cls._gorg is Generator:
             raise TypeError("Type Generator cannot be instantiated; "
                             "create a subclass instead")
         return _generic_new(_G_base, cls, *args, **kwds)
index 5f97728..6be7bb4 100644 (file)
@@ -165,7 +165,7 @@ def _set_signature(mock, original, instance=False):
     skipfirst = isinstance(original, type)
     result = _get_signature_object(original, instance, skipfirst)
     if result is None:
-        return
+        return mock
     func, sig = result
     def checksig(*args, **kwargs):
         sig.bind(*args, **kwargs)
index 1996a8e..48d8fe9 100644 (file)
@@ -528,6 +528,9 @@ class TestDiscovery(unittest.TestCase):
             pickle.loads(pickle.dumps(test, proto))
 
     def test_discover_with_module_that_raises_SkipTest_on_import(self):
+        if not unittest.BaseTestSuite._cleanup:
+            raise unittest.SkipTest("Suite cleanup is disabled")
+
         loader = unittest.TestLoader()
 
         def _get_module_from_name(name):
@@ -548,6 +551,9 @@ class TestDiscovery(unittest.TestCase):
             pickle.loads(pickle.dumps(suite, proto))
 
     def test_discover_with_init_module_that_raises_SkipTest_on_import(self):
+        if not unittest.BaseTestSuite._cleanup:
+            raise unittest.SkipTest("Suite cleanup is disabled")
+
         vfs = {abspath('/foo'): ['my_package'],
                abspath('/foo/my_package'): ['__init__.py', 'test_module.py']}
         self.setup_import_issue_package_tests(vfs)
index d2202a7..7919482 100644 (file)
@@ -1,3 +1,5 @@
+import time
+import types
 import unittest
 
 from unittest.mock import (
@@ -856,6 +858,19 @@ class SpecSignatureTest(unittest.TestCase):
         check_data_descriptor(foo.desc)
 
 
+    def test_autospec_on_bound_builtin_function(self):
+        meth = types.MethodType(time.ctime, time.time())
+        self.assertIsInstance(meth(), str)
+        mocked = create_autospec(meth)
+
+        # no signature, so no spec to check against
+        mocked()
+        mocked.assert_called_once_with()
+        mocked.reset_mock()
+        mocked(4, 5, 6)
+        mocked.assert_called_once_with(4, 5, 6)
+
+
 class TestCallList(unittest.TestCase):
 
     def test_args_list_contains_call_list(self):
index 6faa2d6..3e0dca0 100644 (file)
@@ -264,10 +264,14 @@ class SimpleXMLRPCDispatcher:
         except:
             # report exception back to server
             exc_type, exc_value, exc_tb = sys.exc_info()
-            response = dumps(
-                Fault(1, "%s:%s" % (exc_type, exc_value)),
-                encoding=self.encoding, allow_none=self.allow_none,
-                )
+            try:
+                response = dumps(
+                    Fault(1, "%s:%s" % (exc_type, exc_value)),
+                    encoding=self.encoding, allow_none=self.allow_none,
+                    )
+            finally:
+                # Break reference cycle
+                exc_type = exc_value = exc_tb = None
 
         return response.encode(self.encoding, 'xmlcharrefreplace')
 
@@ -359,10 +363,14 @@ class SimpleXMLRPCDispatcher:
                     )
             except:
                 exc_type, exc_value, exc_tb = sys.exc_info()
-                results.append(
-                    {'faultCode' : 1,
-                     'faultString' : "%s:%s" % (exc_type, exc_value)}
-                    )
+                try:
+                    results.append(
+                        {'faultCode' : 1,
+                         'faultString' : "%s:%s" % (exc_type, exc_value)}
+                        )
+                finally:
+                    # Break reference cycle
+                    exc_type = exc_value = exc_tb = None
         return results
 
     def _dispatch(self, method, params):
@@ -624,10 +632,14 @@ class MultiPathXMLRPCServer(SimpleXMLRPCServer):
             # (each dispatcher should have handled their own
             # exceptions)
             exc_type, exc_value = sys.exc_info()[:2]
-            response = dumps(
-                Fault(1, "%s:%s" % (exc_type, exc_value)),
-                encoding=self.encoding, allow_none=self.allow_none)
-            response = response.encode(self.encoding, 'xmlcharrefreplace')
+            try:
+                response = dumps(
+                    Fault(1, "%s:%s" % (exc_type, exc_value)),
+                    encoding=self.encoding, allow_none=self.allow_none)
+                response = response.encode(self.encoding, 'xmlcharrefreplace')
+            finally:
+                # Break reference cycle
+                exc_type = exc_value = None
         return response
 
 class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher):
index 1fff052..4cb7e32 100755 (executable)
@@ -1088,10 +1088,10 @@ def buildPythonDocs():
     docdir = os.path.join(rootDir, 'pydocs')
     curDir = os.getcwd()
     os.chdir(buildDir)
-    # The Doc build changed for 3.4 (technically, for 3.4.1) and for 2.7.9
     runCommand('make clean')
-    # Assume sphinx-build is on our PATH, checked in checkEnvironment
-    runCommand('make html')
+    # Create virtual environment for docs builds with blurb and sphinx
+    runCommand('make venv')
+    runCommand('make html PYTHON=venv/bin/python')
     os.chdir(curDir)
     if not os.path.exists(docdir):
         os.mkdir(docdir)
index 07f09fa..d419474 100644 (file)
@@ -98,7 +98,7 @@ In general, universal builds depend on specific features provided by the
 Apple-supplied compilers and other build tools included in Apple's Xcode
 development tools.  You should install Xcode and the command line tools
 component appropriate for the OS X release you are running on.  See the
-Python Developer's Guide (http://docs.python.org/devguide/setup.html)
+Python Developer's Guide (https://devguide.python.org/setup/)
 for more information.
 
 2.1 Flavors of universal binaries
@@ -355,4 +355,4 @@ Resources
 
   *  http://www.python.org/community/sigs/current/pythonmac-sig/
 
-  *  http://docs.python.org/devguide/
\ No newline at end of file
+  *  https://devguide.python.org/
index 82e8307..6cb0c63 100644 (file)
@@ -1001,6 +1001,8 @@ TESTPYTHON=       $(RUNSHARED) ./$(BUILDPYTHON) $(TESTPYTHONOPTS)
 TESTRUNNER=    $(TESTPYTHON) $(srcdir)/Tools/scripts/run_tests.py
 TESTTIMEOUT=   1200
 
+.PHONY: test testall testuniversal buildbottest pythoninfo
+
 # Run a basic set of regression tests.
 # This excludes some tests that are particularly resource-intensive.
 test:          @DEF_MAKE_RULE@ platform
@@ -1039,6 +1041,9 @@ buildbottest:     build_all platform
                fi
                $(TESTRUNNER) -j 1 -u all -W --slowest --timeout=$(TESTTIMEOUT) $(TESTOPTS)
 
+pythoninfo: build_all
+               $(RUNSHARED) ./$(BUILDPYTHON) -m test.pythoninfo
+
 QUICKTESTOPTS= $(TESTOPTS) -x test_subprocess test_io test_lib2to3 \
                test_multibytecodec test_urllib2_localnet test_itertools \
                test_multiprocessing_fork test_multiprocessing_spawn \
@@ -1048,6 +1053,13 @@ QUICKTESTOPTS=   $(TESTOPTS) -x test_subprocess test_io test_lib2to3 \
 quicktest:     @DEF_MAKE_RULE@ platform
                $(TESTRUNNER) $(QUICKTESTOPTS)
 
+# SSL tests
+.PHONY: multisslcompile multissltest
+multisslcompile: build_all
+       $(RUNSHARED) ./$(BUILDPYTHON) Tools/ssl/multissltests.py --compile-only
+
+multissltest: build_all
+       $(RUNSHARED) ./$(BUILDPYTHON) Tools/ssl/multissltests.py
 
 install: @FRAMEWORKINSTALLFIRST@ commoninstall bininstall maninstall @FRAMEWORKINSTALLLAST@
        if test "x$(ENSUREPIP)" != "xno"  ; then \
@@ -1206,6 +1218,8 @@ LIBSUBDIRS=       tkinter tkinter/test tkinter/test/test_tkinter \
                test/test_import/data \
                test/test_import/data/circular_imports \
                test/test_import/data/circular_imports/subpkg \
+               test/test_import/data/package \
+               test/test_import/data/package2 \
                test/test_importlib/namespace_pkgs \
                test/test_importlib/namespace_pkgs/both_portions \
                test/test_importlib/namespace_pkgs/both_portions/foo \
index 03f9ea6..83336ec 100644 (file)
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -56,6 +56,7 @@ Ankur Ankan
 Heidi Annexstad
 Ramchandra Apte
 Éric Araujo
+Alexandru Ardelean
 Alicia Arlen
 Jeffrey Armstrong
 Jason Asbahr
@@ -591,6 +592,7 @@ David Harrigan
 Brian Harring
 Jonathan Hartley
 Travis B. Hartwell
+Shane Harvey
 Larry Hastings
 Tim Hatch
 Shane Hathaway
@@ -618,11 +620,13 @@ Thomas Herve
 Bernhard Herzog
 Magnus L. Hetland
 Raymond Hettinger
+Lisa Hewus Fresh
 Kevan Heydon
 Wouter van Heyst
 Kelsey Hightower
 Jason Hildebrand
 Aaron Hill
+Joel Hillacre
 Richie Hindle
 Konrad Hinsen
 David Hobley
@@ -800,6 +804,7 @@ Kim Knapp
 Lenny Kneler
 Pat Knight
 Jeff Knupp
+Nicholas Kobald
 Kubilay Kocak
 Greg Kochanski
 Manvisha Kodali
@@ -1076,6 +1081,7 @@ Vilmos Nebehaj
 Fredrik Nehr
 Tony Nelson
 Trent Nelson
+Osvaldo Santana Neto
 Chad Netzer
 Max Neunhöffer
 Anthon van der Neut
@@ -1166,6 +1172,7 @@ Berker Peksag
 Andreas Pelme
 Steven Pemberton
 Bo Peng
+Bruno "Polaco" Penteado
 Santiago Peresón
 George Peristerakis
 Thomas Perl
@@ -1336,6 +1343,7 @@ Chris Ryland
 Bernt Røskar Brenna
 Constantina S.
 Matthieu S
+Cheryl Sabella
 Patrick Sabin
 Sébastien Sablé
 Amit Saha
@@ -1529,6 +1537,7 @@ Martin Teichmann
 Gustavo Temple
 Mikhail Terekhov
 Victor Terrón
+Pablo Galindo
 Richard M. Tew
 Tobias Thelen
 Févry Thibault
@@ -1579,6 +1588,7 @@ Doobee R. Tzeck
 Eren Türkay
 Lionel Ulmer
 Adnan Umer
+Utkarsh Upadhyay
 Roger Upole
 Daniel Urban
 Michael Urman
@@ -1589,6 +1599,7 @@ Andi Vajda
 Case Van Horsen
 John Mark Vandenberg
 Kyle VanderBeek
+Eric N. Vander Weele
 Andrew Vant
 Atul Varma
 Dmitry Vasiliev
@@ -1688,6 +1699,7 @@ John Wiseman
 Chris Withers
 Stefan Witzel
 Irek Wlizlo
+Charles Wohlganger
 David Wolever
 Klaus-Juergen Wolf
 Dan Wolfe
index 644b4c3..7999521 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
 Python News
 +++++++++++
 
-What's New in Python 3.6.2?
-===========================
+What's New in Python 3.6.3 final?
+=================================
 
-*Release date: 2017-07-17*
+*Release date: 2017-10-03*
 
-- No changes since release candidate 2
+Library
+-------
 
+- bpo-31641: Re-allow arbitrary iterables in
+  `concurrent.futures.as_completed()`. Fixes regression in 3.6.3rc1.
 
-What's New in Python 3.6.2 release candidate 2?
+Build
+-----
+
+- bpo-31662: Fix typos in Windows ``uploadrelease.bat`` script. Fix Windows
+  Doc build issues in ``Doc/make.bat``.
+
+- bpo-31423: Fix building the PDF documentation with newer versions of
+  Sphinx.
+
+
+What's New in Python 3.6.3 release candidate 1?
 ===============================================
 
-*Release date: 2017-07-07*
+*Release date: 2017-09-18*
+
+Security
+--------
+
+- bpo-29781: SSLObject.version() now correctly returns None when handshake
+  over BIO has not been performed yet.
+
+- bpo-30947: Upgrade libexpat embedded copy from version 2.2.1 to 2.2.3 to
+  get security fixes.
 
 Core and Builtins
 -----------------
 
+- bpo-31471: Fix an assertion failure in `subprocess.Popen()` on Windows, in
+  case the env argument has a bad keys() method. Patch by Oren Milman.
+
+- bpo-31418: Fix an assertion failure in `PyErr_WriteUnraisable()` in case
+  of an exception with a bad ``__module__`` attribute. Patch by Oren Milman.
+
+- bpo-31416: Fix assertion failures in case of a bad warnings.filters or
+  warnings.defaultaction. Patch by Oren Milman.
+
+- bpo-31411: Raise a TypeError instead of SystemError in case
+  warnings.onceregistry is not a dictionary. Patch by Oren Milman.
+
+- bpo-31373: Fix several possible instances of undefined behavior due to
+  floating-point demotions.
+
+- bpo-30465: Location information (``lineno`` and ``col_offset``) in
+  f-strings is now (mostly) correct.  This fixes tools like flake8 from
+  showing warnings on the wrong line (typically the first line of the file).
+
+- bpo-31343: Include sys/sysmacros.h for major(), minor(), and makedev().
+  GNU C libray plans to remove the functions from sys/types.h.
+
+- bpo-31291: Fix an assertion failure in `zipimport.zipimporter.get_data` on
+  Windows, when the return value of ``pathname.replace('/','\\')`` isn't a
+  string. Patch by Oren Milman.
+
+- bpo-31271: Fix an assertion failure in the write() method of
+  `io.TextIOWrapper`, when the encoder doesn't return a bytes object. Patch
+  by Oren Milman.
+
+- bpo-31243: Fix a crash in some methods of `io.TextIOWrapper`, when the
+  decoder's state is invalid. Patch by Oren Milman.
+
+- bpo-30721: ``print`` now shows correct usage hint for using Python 2
+  redirection syntax.  Patch by Sanyam Khurana.
+
+- bpo-31070: Fix a race condition in importlib _get_module_lock().
+
+- bpo-31095: Fix potential crash during GC caused by ``tp_dealloc`` which
+  doesn't call ``PyObject_GC_UnTrack()``.
+
+- bpo-31071: Avoid masking original TypeError in call with * unpacking when
+  other arguments are passed.
+
+- bpo-30978: str.format_map() now passes key lookup exceptions through.
+  Previously any exception was replaced with a KeyError exception.
+
+- bpo-30808: Use _Py_atomic API for concurrency-sensitive signal state.
+
+- bpo-30876: Relative import from unloaded package now reimports the package
+  instead of failing with SystemError.  Relative import from non-package now
+  fails with ImportError rather than SystemError.
+
+- bpo-30703: Improve signal delivery.
+
+  Avoid using Py_AddPendingCall from signal handler, to avoid calling
+  signal- unsafe functions. The tests I'm adding here fail without the rest
+  of the patch, on Linux and OS X. This means our signal delivery logic had
+  defects (some signals could be lost).
+
+- bpo-30765: Avoid blocking in pthread_mutex_lock() when
+  PyThread_acquire_lock() is asked not to block.
+
+- bpo-31161: Make sure the 'Missing parentheses' syntax error message is
+  only applied to SyntaxError, not to subclasses. Patch by Martijn Pieters.
+
+- bpo-30814: Fixed a race condition when import a submodule from a package.
+
+- bpo-30597: ``print`` now shows expected input in custom error message when
+  used as a Python 2 statement. Patch by Sanyam Khurana.
+
 Library
 -------
 
-- [Security] bpo-30730: Prevent environment variables injection in subprocess on
-  Windows.  Prevent passing other environment variables and command arguments.
+- bpo-31499: xml.etree: Fix a crash when a parser is part of a reference
+  cycle.
+
+- bpo-28556: typing.get_type_hints now finds the right globalns for classes
+  and modules by default (when no ``globalns`` was specified by the caller).
+
+- bpo-28556: Speed improvements to the ``typing`` module.  Original PRs by
+  Ivan Levkivskyi and Mitar.
+
+- bpo-31544: The C accelerator module of ElementTree ignored exceptions
+  raised when looking up TreeBuilder target methods in XMLParser().
+
+- bpo-31234: socket.create_connection() now fixes manually a reference
+  cycle: clear the variable storing the last exception on success.
+
+- bpo-31457: LoggerAdapter objects can now be nested.
+
+- bpo-31400: Improves SSL error handling to avoid losing error numbers.
+
+- bpo-28958: ssl.SSLContext() now uses OpenSSL error information when a
+  context cannot be instantiated.
+
+- bpo-27340: SSLSocket.sendall() now uses memoryview to create slices of
+  data. This fixes support for all bytes-like object. It is also more
+  efficient and avoids costly copies.
+
+- bpo-31178: Fix string concatenation bug in rare error path in the
+  subprocess module
+
+- bpo-31350: Micro-optimize :func:`asyncio._get_running_loop` to become up
+  to 10% faster.
+
+- bpo-31170: expat: Update libexpat from 2.2.3 to 2.2.4. Fix copying of
+  partial characters for UTF-8 input (libexpat bug 115):
+  https://github.com/libexpat/libexpat/issues/115
+
+- bpo-29136: Add TLS 1.3 cipher suites and OP_NO_TLSv1_3.
+
+- bpo-29212: Fix concurrent.futures.thread.ThreadPoolExecutor threads to
+  have a non repr() based thread name by default when no thread_name_prefix
+  is supplied. They will now identify themselves as "ThreadPoolExecutor-
+  y_n".
+
+- bpo-9146: Fix a segmentation fault in _hashopenssl when standard hash
+  functions such as md5 are not available in the linked OpenSSL library.  As
+  in some special FIPS-140 build environments.
+
+- bpo-27144: The ``map()`` and ``as_completed()`` iterators in
+  ``concurrent.futures`` now avoid keeping a reference to yielded objects.
+
+- bpo-10746: Fix ctypes producing wrong PEP 3118 type codes for integer
+  types.
+
+- bpo-22536: The subprocess module now sets the filename when
+  FileNotFoundError is raised on POSIX systems due to the executable or cwd
+  not being found.
+
+- bpo-31249: concurrent.futures: WorkItem.run() used by ThreadPoolExecutor
+  now breaks a reference cycle between an exception object and the WorkItem
+  object.
+
+- bpo-31247: xmlrpc.server now explicitly breaks reference cycles when using
+  sys.exc_info() in code handling exceptions.
+
+- bpo-30102: The ssl and hashlib modules now call
+  OPENSSL_add_all_algorithms_noconf() on OpenSSL < 1.1.0. The function
+  detects CPU features and enables optimizations on some CPU architectures
+  such as POWER8. Patch is based on research from Gustavo Serra Scalet.
+
+- bpo-31185: Fixed miscellaneous errors in asyncio speedup module.
+
+- bpo-31135: ttk: fix the destroy() method of LabeledScale and OptionMenu
+  classes. Call the parent destroy() method even if the used attribute
+  doesn't exist. The LabeledScale.destroy() method now also explicitly
+  clears label and scale attributes to help the garbage collector to destroy
+  all widgets.
+
+- bpo-31107: Fix `copyreg._slotnames()` mangled attribute calculation for
+  classes whose name begins with an underscore. Patch by Shane Harvey.
+
+- bpo-31061: Fixed a crash when using asyncio and threads.
+
+- bpo-30502: Fix handling of long oids in ssl.  Based on patch by Christian
+  Heimes.
+
+- bpo-30119: ftplib.FTP.putline() now throws ValueError on commands that
+  contains CR or LF. Patch by Dong-hee Na.
+
+- bpo-30595: multiprocessing.Queue.get() with a timeout now polls its reader
+  in non- blocking mode if it succeeded to aquire the lock but the acquire
+  took longer than the timeout.
+
+- bpo-29403: Fix ``unittest.mock``'s autospec to not fail on method-bound
+  builtin functions.  Patch by Aaron Gallagher.
+
+- bpo-30961: Fix decrementing a borrowed reference in tracemalloc.
+
+- bpo-25684: Change ``ttk.OptionMenu`` radiobuttons to be unique across
+  instances of ``OptionMenu``.
+
+- bpo-30886: Fix multiprocessing.Queue.join_thread(): it now waits until the
+  thread completes, even if the thread was started by the same process which
+  created the queue.
+
+- bpo-29854: Fix segfault in readline when using readline's history-size
+  option.  Patch by Nir Soffer.
+
+- bpo-30319: socket.close() now ignores ECONNRESET error.
+
+- bpo-30828: Fix out of bounds write in
+  `asyncio.CFuture.remove_done_callback()`.
+
+- bpo-30807: signal.setitimer() may disable the timer when passed a tiny
+  value.
+
+  Tiny values (such as 1e-6) are valid non-zero values for setitimer(),
+  which is specified as taking microsecond-resolution intervals. However, on
+  some platform, our conversion routine could convert 1e-6 into a zero
+  interval, therefore disabling the timer instead of (re-)scheduling it.
+
+- bpo-30441: Fix bug when modifying os.environ while iterating over it
+
+- bpo-30532: Fix email header value parser dropping folding white space in
+  certain cases.
+
+- bpo-30879: os.listdir() and os.scandir() now emit bytes names when called
+  with bytes- like argument.
+
+- bpo-30746: Prohibited the '=' character in environment variable names in
+  ``os.putenv()`` and ``os.spawn*()``.
+
+- bpo-29755: Fixed the lgettext() family of functions in the gettext module.
+  They now always return bytes.
+
+Documentation
+-------------
+
+- bpo-31294: Fix incomplete code snippet in the ZeroMQSocketListener and
+  ZeroMQSocketHandler examples and adapt them to Python 3.
+
+- bpo-21649: Add RFC 7525 and Mozilla server side TLS links to SSL
+  documentation.
+
+- bpo-30803: Clarify doc on truth value testing. Original patch by Peter
+  Thomassen.
+
+Tests
+-----
+
+- bpo-31320: Silence traceback in test_ssl
+
+- bpo-25674: Remove sha256.tbs-internet.com ssl test
+
+- bpo-30715: Address ALPN callback changes for OpenSSL 1.1.0f. The latest
+  version behaves like OpenSSL 1.0.2 and no longer aborts handshake.
+
+- bpo-30822: regrtest: Exclude tzdata from regrtest --all. When running the
+  test suite using --use=all / -u all, exclude tzdata since it makes
+  test_datetime too slow (15-20 min on some buildbots) which then times out
+  on some buildbots. Fix also regrtest command line parser to allow passing
+  -u extralargefile to run test_zipfile64.
+
+Build
+-----
+
+- bpo-30854: Fix compile error when compiling --without-threads. Patch by
+  Masayuki Yamamoto.
+
+Windows
+-------
+
+- bpo-30389: Adds detection of Visual Studio 2017 to distutils on Windows.
+
+- bpo-31340: Change to building with MSVC v141 (included with Visual Studio
+  2017)
+
+- bpo-30581: os.cpu_count() now returns the correct number of processors on
+  Windows when the number of logical processors is greater than 64.
+
+- bpo-30731: Add a missing xmlns to python.manifest so that it matches the
+  schema.
+
+IDLE
+----
+
+- bpo-31493: IDLE code context -- fix code update and font update timers.
+
+  Canceling timers prevents a warning message when test_idle completes.
+
+- bpo-31488: IDLE - Update non-key options in former extension classes. When
+  applying configdialog changes, call .reload for each feature class. Change
+  ParenMatch so updated options affect existing instances attached to
+  existing editor windows.
+
+- bpo-31477: IDLE - Improve rstrip entry in doc. Strip trailing whitespace
+  strips more than blank spaces.  Multiline string literals are not skipped.
+
+- bpo-31480: IDLE - make tests pass with zzdummy extension disabled by
+  default.
+
+- bpo-31421: Document how IDLE runs tkinter programs. IDLE calls tcl/tk
+  update in the background in order to make live
+
+  interaction and experimentatin with tkinter applications much easier.
+
+- bpo-31414: IDLE -- fix tk entry box tests by deleting first. Adding to an
+  int entry is not the same as deleting and inserting because int('') will
+  fail.
+
+- bpo-31051: Rearrange IDLE condigdialog GenPage into Window, Editor, and
+  Help sections.
+
+- bpo-30617: IDLE - Add docstrings and tests for outwin subclass of editor.
+
+  Move some data and functions from the class to module level. Patch by
+  Cheryl Sabella.
+
+- bpo-31287: IDLE - Do not modify tkinter.message in test_configdialog.
+
+- bpo-27099: Convert IDLE's built-in 'extensions' to regular features.
+
+  About 10 IDLE features were implemented as supposedly optional extensions.
+  Their different behavior could be confusing or worse for users and not
+  good for maintenance.  Hence the conversion.
+
+  The main difference for users is that user configurable key bindings for
+  builtin features are now handled uniformly.  Now, editing a binding in a
+  keyset only affects its value in the keyset.  All bindings are defined
+  together in the system-specific default keysets in config- extensions.def.
+  All custom keysets are saved as a whole in config- extension.cfg.  All
+  take effect as soon as one clicks Apply or Ok.
+
+  The affected events are '<<force-open-completions>>', '<<expand-word>>',
+  '<<force-open-calltip>>', '<<flash-paren>>', '<<format-paragraph>>',
+  '<<run- module>>', '<<check-module>>', and '<<zoom-height>>'.  Any
+  (global) customizations made before 3.6.3 will not affect their keyset-
+  specific customization after 3.6.3. and vice versa.
+
+  Inital patch by Charles Wohlganger.
+
+- bpo-31206: IDLE: Factor HighPage(Frame) class from ConfigDialog. Patch by
+  Cheryl Sabella.
+
+- bpo-31001: Add tests for configdialog highlight tab.  Patch by Cheryl
+  Sabella.
+
+- bpo-31205: IDLE: Factor KeysPage(Frame) class from ConfigDialog.  The
+  slightly modified tests continue to pass.  Patch by Cheryl Sabella.
+
+- bpo-31130: IDLE -- stop leaks in test_configdialog. Initial patch by
+  Victor Stinner.
+
+- bpo-31002: Add tests for configdialog keys tab. Patch by Cheryl Sabella.
+
+- bpo-19903: IDLE: Calltips use `inspect.signature` instead of
+  `inspect.getfullargspec`. This improves calltips for builtins converted to
+  use Argument Clinic. Patch by Louie Lu.
+
+- bpo-31083: IDLE - Add an outline of a TabPage class in configdialog.
+  Update existing classes to match outline. Initial patch by Cheryl Sabella.
+
+- bpo-31050: Factor GenPage(Frame) class from ConfigDialog. The slightly
+  modified tests continue to pass. Patch by Cheryl Sabella.
 
-- [Security] bpo-30694: Upgrade expat copy from 2.2.0 to 2.2.1 to get fixes
-  of multiple security vulnerabilities including: CVE-2017-9233 (External
-  entity infinite loop DoS), CVE-2016-9063 (Integer overflow, re-fix),
-  CVE-2016-0718 (Fix regression bugs from 2.2.0's fix to CVE-2016-0718)
-  and CVE-2012-0876 (Counter hash flooding with SipHash).
-  Note: the CVE-2016-5300 (Use os-specific entropy sources like getrandom)
-  doesn't impact Python, since Python already gets entropy from the OS to set
-  the expat secret using ``XML_SetHashSalt()``.
+- bpo-31004: IDLE - Factor FontPage(Frame) class from ConfigDialog.
 
-- [Security] bpo-30500: Fix urllib.parse.splithost() to correctly parse
-  fragments. For example, ``splithost('//127.0.0.1#@evil.com/')`` now
-  correctly returns the ``127.0.0.1`` host, instead of treating ``@evil.com``
-  as the host in an authentification (``login@host``).
+  Slightly modified tests continue to pass. Fix General tests. Patch mostly
+  by Cheryl Sabella.
+
+- bpo-30781: IDLE - Use ttk widgets in ConfigDialog. Patches by Terry Jan
+  Reedy and Cheryl Sabella.
+
+- bpo-31060: IDLE - Finish rearranging methods of ConfigDialog Grouping
+  methods pertaining to each tab and the buttons will aid writing tests and
+  improving the tabs and will enable splitting the groups into classes.
+
+- bpo-30853: IDLE -- Factor a VarTrace class out of ConfigDialog.
+
+  Instance tracers manages pairs consisting of a tk variable and a callback
+  function.  When tracing is turned on, setting the variable calls the
+  function.  Test coverage for the new class is 100%.
+
+- bpo-31003: IDLE: Add more tests for General tab.
+
+- bpo-30993: IDLE - Improve configdialog font page and tests.
+
+  In configdialog: Document causal pathways in create_font_tab docstring.
+  Simplify some attribute names. Move set_samples calls to var_changed_font
+  (idea from Cheryl Sabella).  Move related functions to positions after the
+  create widgets function.
+
+  In test_configdialog: Fix test_font_set so not order dependent.  Fix
+  renamed test_indent_scale so it tests the widget.  Adjust tests for
+  movement of set_samples call.  Add tests for load functions.  Put all font
+  tests in one class and tab indent tests in another.  Except for two lines,
+  these tests completely cover the related functions.
+
+- bpo-30981: IDLE -- Add more configdialog font page tests.
+
+- bpo-28523: IDLE: replace 'colour' with 'color' in configdialog.
+
+- bpo-30917: Add tests for idlelib.config.IdleConf. Increase coverage from
+  46% to 96%. Patch by Louie Lu.
+
+- bpo-30934: Document coverage details for idlelib tests.
+
+  * Add section to idlelib/idle-test/README.txt.
+
+  * Include check that branches are taken both ways.
+
+  * Exclude IDLE-specific code that does not run during unit tests.
+
+- bpo-30913: IDLE: Document ConfigDialog tk Vars, methods, and widgets in
+  docstrings This will facilitate improving the dialog and splitting up the
+  class. Original patch by Cheryl Sabella.
+
+- bpo-30899: IDLE: Add tests for ConfigParser subclasses in config. Patch by
+  Louie Lu.
+
+- bpo-30881: IDLE: Add docstrings to browser.py. Patch by Cheryl Sabella.
+
+- bpo-30851: IDLE: Remove unused variables in configdialog.  One is a
+  duplicate, one is set but cannot be altered by users. Patch by Cheryl
+  Sabella.
+
+- bpo-30870: IDLE: In Settings dialog, select font with Up, Down keys as
+  well as mouse. Initial patch by Louie Lu.
+
+- bpo-8231: IDLE: call config.IdleConf.GetUserCfgDir only once.
+
+- bpo-30779: IDLE: Factor ConfigChanges class from configdialog, put in
+  config; test. * In config, put dump test code in a function; run it and
+  unittest in   'if __name__ == '__main__'. * Add class config.ConfigChanges
+  based on changes_class_v4.py on bpo issue. * Add class
+  test_config.ChangesTest, partly using configdialog_tests_v1.py. * Revise
+  configdialog to use ConfigChanges; see tracker msg297804. * Revise
+  test_configdialog to match configdialog changes. * Remove configdialog
+  functions unused or moved to ConfigChanges. Cheryl Sabella contributed
+  parts of the patch.
+
+- bpo-30777: IDLE: configdialog - Add docstrings and fix comments. Patch by
+  Cheryl Sabella.
+
+- bpo-30495: IDLE: Improve textview with docstrings, PEP8 names, and more
+  tests. Patch by Cheryl Sabella.
+
+- bpo-30723: IDLE: Make several improvements to parenmatch. Add 'parens'
+  style to highlight both opener and closer. Make 'default' style, which is
+  not default, a synonym for 'opener'. Make time-delay work the same with
+  all styles. Add help for config dialog extensions tab, including help for
+  parenmatch. Add new tests.  Original patch by Charles Wohlganger.
+
+- bpo-30674: IDLE: add docstrings to grep module. Patch by Cheryl Sabella
+
+- bpo-21519: IDLE's basic custom key entry dialog now detects duplicates
+  properly. Original patch by Saimadhav Heblikar.
+
+- bpo-29910: IDLE no longer deletes a character after commenting out a
+  region by a key shortcut.  Add ``return 'break'`` for this and other
+  potential conflicts between IDLE and default key bindings.
+
+- bpo-30728: Review and change idlelib.configdialog names. Lowercase method
+  and attribute names. Replace 'colour' with 'color', expand overly cryptic
+  names, delete unneeded underscores. Replace ``import *`` with specific
+  imports. Patches by Cheryl Sabella.
+
+- bpo-6739: IDLE: Verify user-entered key sequences by trying to bind them
+  with tk. Add tests for all 3 validation functions. Original patch by G
+  Polo.  Tests added by Cheryl Sabella.
+
+Tools/Demos
+-----------
+
+- bpo-30983: gdb integration commands (py-bt, etc.) work on optimized shared
+  builds now, too.  PEP 523 introduced _PyEval_EvalFrameDefault which
+  inlines PyEval_EvalFrameEx on non-debug shared builds.  This broke the
+  ability to use py-bt, py-up, and a few other Python-specific gdb
+  integrations. The problem is fixed by only looking for
+  _PyEval_EvalFrameDefault frames in python-gdb.py.  Original patch by Bruno
+  "Polaco" Penteado.
+
+
+What's New in Python 3.6.2 final?
+=================================
+
+*Release date: 2017-07-17*
+
+No changes since release candidate 2
+
+
+
+What's New in Python 3.6.2 release candidate 2?
+===============================================
+
+*Release date: 2017-07-07*
+
+Security
+--------
+
+- bpo-30730: Prevent environment variables injection in subprocess on
+  Windows.  Prevent passing other environment variables and command
+  arguments.
+
+- bpo-30694: Upgrade expat copy from 2.2.0 to 2.2.1 to get fixes of multiple
+  security vulnerabilities including: CVE-2017-9233 (External entity
+  infinite loop DoS), CVE-2016-9063 (Integer overflow, re-fix),
+  CVE-2016-0718 (Fix regression bugs from 2.2.0's fix to CVE-2016-0718) and
+  CVE-2012-0876 (Counter hash flooding with SipHash). Note: the
+  CVE-2016-5300 (Use os- specific entropy sources like getrandom) doesn't
+  impact Python, since Python already gets entropy from the OS to set the
+  expat secret using ``XML_SetHashSalt()``.
+
+- bpo-30500: Fix urllib.parse.splithost() to correctly parse fragments. For
+  example, ``splithost('//127.0.0.1#@evil.com/')`` now correctly returns the
+  ``127.0.0.1`` host, instead of treating ``@evil.com`` as the host in an
+  authentification (``login@host``).
 
 
 What's New in Python 3.6.2 release candidate 1?
@@ -47,8 +540,8 @@ What's New in Python 3.6.2 release candidate 1?
 Core and Builtins
 -----------------
 
-- bpo-30682: Removed a too-strict assertion that failed for certain f-strings,
-  such as eval("f'\\\n'") and eval("f'\\\r'").
+- bpo-30682: Removed a too-strict assertion that failed for certain
+  f-strings, such as eval("f'\\\n'") and eval("f'\\\r'").
 
 - bpo-30604: Move co_extra_freefuncs to not be per-thread to avoid crashes
 
@@ -58,53 +551,59 @@ Core and Builtins
   mutated during searching, inserting or comparing.  Based on patches by
   Duane Griffin and Tim Mitchell.
 
-- bpo-25794: Fixed type.__setattr__() and type.__delattr__() for
-  non-interned attribute names.  Based on patch by Eryk Sun.
+- bpo-25794: Fixed type.__setattr__() and type.__delattr__() for non-
+  interned attribute names.  Based on patch by Eryk Sun.
 
-- bpo-30039: If a KeyboardInterrupt happens when the interpreter is in
-  the middle of resuming a chain of nested 'yield from' or 'await'
-  calls, it's now correctly delivered to the innermost frame.
+- bpo-30039: If a KeyboardInterrupt happens when the interpreter is in the
+  middle of resuming a chain of nested 'yield from' or 'await' calls, it's
+  now correctly delivered to the innermost frame.
 
-- bpo-12414: sys.getsizeof() on a code object now returns the sizes
-  which includes the code struct and sizes of objects which it references.
-  Patch by Dong-hee Na.
+- bpo-12414: sys.getsizeof() on a code object now returns the sizes which
+  includes the code struct and sizes of objects which it references. Patch
+  by Dong-hee Na.
 
 - bpo-29949: Fix memory usage regression of set and frozenset object.
 
-- bpo-29935: Fixed error messages in the index() method of tuple, list and deque
-  when pass indices of wrong type.
+- bpo-29935: Fixed error messages in the index() method of tuple, list and
+  deque when pass indices of wrong type.
 
 - bpo-29859: Show correct error messages when any of the pthread_* calls in
   thread_pthread.h fails.
 
-- bpo-28876: ``bool(range)`` works even if ``len(range)``
-  raises :exc:`OverflowError`.
+- bpo-28876: ``bool(range)`` works even if ``len(range)`` raises
+  :exc:`OverflowError`.
 
 - bpo-29600: Fix wrapping coroutine return values in StopIteration.
 
-- bpo-28856: Fix an oversight that %b format for bytes should support objects
-  follow the buffer protocol.
+- bpo-28856: Fix an oversight that %b format for bytes should support
+  objects follow the buffer protocol.
 
-- bpo-29714: Fix a regression that bytes format may fail when containing zero
-  bytes inside.
+- bpo-29714: Fix a regression that bytes format may fail when containing
+  zero bytes inside.
 
-- bpo-29478: If max_line_length=None is specified while using the Compat32 policy,
-  it is no longer ignored.  Patch by Mircea Cosbuc.
+- bpo-29478: If max_line_length=None is specified while using the Compat32
+  policy, it is no longer ignored.  Patch by Mircea Cosbuc.
 
 Library
 -------
 
+- bpo-30616: Functional API of enum allows to create empty enums. Patched by
+  Dong-hee Na
+
 - bpo-30038: Fix race condition between signal delivery and wakeup file
-  descriptor.  Patch by Nathaniel Smith.
+  descriptor. Patch by Nathaniel Smith.
 
 - bpo-23894: lib2to3 now recognizes ``rb'...'`` and ``f'...'`` strings.
 
-- bpo-23890: unittest.TestCase.assertRaises() now manually breaks a reference
-  cycle to not keep objects alive longer than expected.
+- bpo-23890: unittest.TestCase.assertRaises() now manually breaks a
+  reference cycle to not keep objects alive longer than expected.
 
-- bpo-30149: inspect.signature() now supports callables with
-  variable-argument parameters wrapped with partialmethod.
-  Patch by Dong-hee Na.
+- bpo-30149: inspect.signature() now supports callables with variable-
+  argument parameters wrapped with partialmethod. Patch by Dong-hee Na.
+
+- bpo-30645: Fix path calculation in imp.load_package(), fixing it for cases
+  when a package is only shipped with bytecodes. Patch by Alexandru
+  Ardelean.
 
 - bpo-29931: Fixed comparison check for ipaddress.ip_interface objects.
   Patch by Sanjay Sundaresan.
@@ -112,14 +611,20 @@ Library
 - bpo-30605: re.compile() no longer raises a BytesWarning when compiling a
   bytes instance with misplaced inline modifier.  Patch by Roy Williams.
 
-- [Security] bpo-29591: Update expat copy from 2.1.1 to 2.2.0 to get fixes
-  of CVE-2016-0718 and CVE-2016-4472. See
+Security
+--------
+
+- bpo-29591: Update expat copy from 2.1.1 to 2.2.0 to get fixes of
+  CVE-2016-0718 and CVE-2016-4472. See
   https://sourceforge.net/p/expat/bugs/537/ for more information.
 
+Library
+-------
+
 - bpo-24484: Avoid race condition in multiprocessing cleanup (#2159)
 
-- bpo-28994: The traceback no longer displayed for SystemExit raised in
-  callback registered by atexit.
+- bpo-28994: The traceback no longer displayed for SystemExit raised in a
+  callback registered by atexit.
 
 - bpo-30508: Don't log exceptions if Task/Future "cancel()" method was
   called.
@@ -134,17 +639,19 @@ Library
 - bpo-29743: Closing transport during handshake process leaks open socket.
   Patch by Nikolay Kim
 
-- bpo-27585: Fix waiter cancellation in asyncio.Lock.
-  Patch by Mathieu Sornay.
+- bpo-27585: Fix waiter cancellation in asyncio.Lock. Patch by Mathieu
+  Sornay.
 
-- bpo-30418: On Windows, subprocess.Popen.communicate() now also ignore EINVAL
-  on stdin.write() if the child process is still running but closed the pipe.
+- bpo-30418: On Windows, subprocess.Popen.communicate() now also ignore
+  EINVAL on stdin.write() if the child process is still running but closed
+  the pipe.
 
 - bpo-29822: inspect.isabstract() now works during __init_subclass__.  Patch
   by Nate Soares.
 
-- bpo-29581: ABCMeta.__new__ now accepts ``**kwargs``, allowing abstract base
-  classes to use keyword parameters in __init_subclass__. Patch by Nate Soares.
+- bpo-29581: ABCMeta.__new__ now accepts ``**kwargs``, allowing abstract
+  base classes to use keyword parameters in __init_subclass__. Patch by Nate
+  Soares.
 
 - bpo-30557: faulthandler now correctly filters and displays exception codes
   on Windows
@@ -152,71 +659,75 @@ Library
 - bpo-30378: Fix the problem that logging.handlers.SysLogHandler cannot
   handle IPv6 addresses.
 
-- bpo-29960: Preserve generator state when _random.Random.setstate()
-  raises an exception.  Patch by Bryan Olson.
+- bpo-29960: Preserve generator state when _random.Random.setstate() raises
+  an exception. Patch by Bryan Olson.
 
-- bpo-30414: multiprocessing.Queue._feed background running
-  thread do not break from main loop on exception.
+- bpo-30414: multiprocessing.Queue._feed background running thread do not
+  break from main loop on exception.
 
-- bpo-30003: Fix handling escape characters in HZ codec.  Based on patch
-  by Ma Lin.
+- bpo-30003: Fix handling escape characters in HZ codec.  Based on patch by
+  Ma Lin.
 
-- bpo-30301: Fix AttributeError when using SimpleQueue.empty() under
-  *spawn* and *forkserver* start methods.
+- bpo-30301: Fix AttributeError when using SimpleQueue.empty() under *spawn*
+  and *forkserver* start methods.
 
 - bpo-30329: imaplib and poplib now catch the Windows socket WSAEINVAL error
   (code 10022) on shutdown(SHUT_RDWR): An invalid operation was attempted.
   This error occurs sometimes on SSL connections.
 
 - bpo-30375: Warnings emitted when compile a regular expression now always
-  point to the line in the user code.  Previously they could point into inners
-  of the re module if emitted from inside of groups or conditionals.
+  point to the line in the user code.  Previously they could point into
+  inners of the re module if emitted from inside of groups or conditionals.
 
-- bpo-30048: Fixed ``Task.cancel()`` can be ignored when the task is
-  running coroutine and the coroutine returned without any more ``await``.
+- bpo-30048: Fixed ``Task.cancel()`` can be ignored when the task is running
+  coroutine and the coroutine returned without any more ``await``.
 
-- bpo-30266: contextlib.AbstractContextManager now supports anti-registration
-  by setting __enter__ = None or __exit__ = None, following the pattern
-  introduced in bpo-25958. Patch by Jelle Zijlstra.
+- bpo-30266: contextlib.AbstractContextManager now supports anti-
+  registration by setting __enter__ = None or __exit__ = None, following the
+  pattern introduced in bpo-25958. Patch by Jelle Zijlstra.
 
-- bpo-30298: Weaken the condition of deprecation warnings for inline modifiers.
-  Now allowed several subsequential inline modifiers at the start of the
-  pattern (e.g. ``'(?i)(?s)...'``).  In verbose mode whitespaces and comments
-  now are allowed before and between inline modifiers (e.g.
-  ``'(?x) (?i) (?s)...'``).
+- bpo-30298: Weaken the condition of deprecation warnings for inline
+  modifiers. Now allowed several subsequential inline modifiers at the start
+  of the pattern (e.g. ``'(?i)(?s)...'``).  In verbose mode whitespaces and
+  comments now are allowed before and between inline modifiers (e.g. ``'(?x)
+  (?i) (?s)...'``).
 
-- bpo-29990: Fix range checking in GB18030 decoder.  Original patch by Ma Lin.
+- bpo-29990: Fix range checking in GB18030 decoder.  Original patch by Ma
+  Lin.
 
-- Revert bpo-26293 for zipfile breakage. See also bpo-29094.
+- bpo-26293: Change resulted because of zipfile breakage. (See also:
+  bpo-29094)
 
 - bpo-30243: Removed the __init__ methods of _json's scanner and encoder.
-  Misusing them could cause memory leaks or crashes.  Now scanner and encoder
-  objects are completely initialized in the __new__ methods.
+  Misusing them could cause memory leaks or crashes.  Now scanner and
+  encoder objects are completely initialized in the __new__ methods.
 
 - bpo-30185: Avoid KeyboardInterrupt tracebacks in forkserver helper process
   when Ctrl-C is received.
 
 - bpo-28556: Various updates to typing module: add typing.NoReturn type, use
-  WrapperDescriptorType, minor bug-fixes.  Original PRs by
-  Jim Fasarakis-Hilliard and Ivan Levkivskyi.
+  WrapperDescriptorType, minor bug-fixes.  Original PRs by Jim Fasarakis-
+  Hilliard and Ivan Levkivskyi.
 
 - bpo-30205: Fix getsockname() for unbound AF_UNIX sockets on Linux.
 
-- bpo-30070: Fixed leaks and crashes in errors handling in the parser module.
+- bpo-30070: Fixed leaks and crashes in errors handling in the parser
+  module.
 
 - bpo-30061: Fixed crashes in IOBase methods __next__() and readlines() when
-  readline() or __next__() respectively return non-sizeable object.
-  Fixed possible other errors caused by not checking results of PyObject_Size(),
+  readline() or __next__() respectively return non-sizeable object. Fixed
+  possible other errors caused by not checking results of PyObject_Size(),
   PySequence_Size(), or PyMapping_Size().
 
-- bpo-30017: Allowed calling the close() method of the zip entry writer object
-  multiple times.  Writing to a closed writer now always produces a ValueError.
+- bpo-30017: Allowed calling the close() method of the zip entry writer
+  object multiple times.  Writing to a closed writer now always produces a
+  ValueError.
 
-- bpo-30068: _io._IOBase.readlines will check if it's closed first when
-  hint is present.
+- bpo-30068: _io._IOBase.readlines will check if it's closed first when hint
+  is present.
 
-- bpo-29694: Fixed race condition in pathlib mkdir with flags
-  parents=True.  Patch by Armin Rigo.
+- bpo-29694: Fixed race condition in pathlib mkdir with flags parents=True.
+  Patch by Armin Rigo.
 
 - bpo-29692: Fixed arbitrary unchaining of RuntimeError exceptions in
   contextlib.contextmanager.  Patch by Siddharth Velankar.
@@ -237,8 +748,8 @@ Library
   exception at the very first of an iterable may swallow the exception or
   make the program hang. Patch by Davin Potts and Xiang Zhang.
 
-- bpo-25803: Avoid incorrect errors raised by Path.mkdir(exist_ok=True)
-  when the OS gives priority to errors such as EACCES over EEXIST.
+- bpo-25803: Avoid incorrect errors raised by Path.mkdir(exist_ok=True) when
+  the OS gives priority to errors such as EACCES over EEXIST.
 
 - bpo-29861: Release references to tasks, their arguments and their results
   as soon as they are finished in multiprocessing.Pool.
@@ -251,14 +762,16 @@ Library
 - bpo-29800: Fix crashes in partial.__repr__ if the keys of partial.keywords
   are not strings.  Patch by Michael Seifert.
 
-- bpo-29742: get_extra_info() raises exception if get called on closed ssl transport.
-  Patch by Nikolay Kim.
+- bpo-29742: get_extra_info() raises exception if get called on closed ssl
+  transport. Patch by Nikolay Kim.
 
-- bpo-8256: Fixed possible failing or crashing input() if attributes "encoding"
-  or "errors" of sys.stdin or sys.stdout are not set or are not strings.
+- bpo-8256: Fixed possible failing or crashing input() if attributes
+  "encoding" or "errors" of sys.stdin or sys.stdout are not set or are not
+  strings.
 
-- bpo-28298: Fix a bug that prevented array 'Q', 'L' and 'I' from accepting big
-  intables (objects that have __int__) as elements.  Patch by Oren Milman.
+- bpo-28298: Fix a bug that prevented array 'Q', 'L' and 'I' from accepting
+  big intables (objects that have __int__) as elements.  Patch by Oren
+  Milman.
 
 - bpo-28231: The zipfile module now accepts path-like objects for external
   paths.
@@ -267,60 +780,57 @@ Library
   check identity before checking equality when do comparisons.
 
 - bpo-29615: SimpleXMLRPCDispatcher no longer chains KeyError (or any other
-  exception) to exception(s) raised in the dispatched methods.
-  Patch by Petr Motejlek.
+  exception) to exception(s) raised in the dispatched methods. Patch by Petr
+  Motejlek.
 
-- bpo-30177: path.resolve(strict=False) no longer cuts the path after the first
-  element not present in the filesystem.  Patch by Antoine Pietri.
+- bpo-30177: path.resolve(strict=False) no longer cuts the path after the
+  first element not present in the filesystem.  Patch by Antoine Pietri.
 
 IDLE
 ----
 
-- bpo-15786: Fix several problems with IDLE's autocompletion box.
-  The following should now work: clicking on selection box items;
-  using the scrollbar; selecting an item by hitting Return.
-  Hangs on MacOSX should no longer happen. Patch by Louie Lu.
+- bpo-15786: Fix several problems with IDLE's autocompletion box. The
+  following should now work: clicking on selection box items; using the
+  scrollbar; selecting an item by hitting Return. Hangs on MacOSX should no
+  longer happen. Patch by Louie Lu.
 
-- bpo-25514: Add doc subsubsection about IDLE failure to start.
-  Popup no-connection message directs users to this section.
+- bpo-25514: Add doc subsubsection about IDLE failure to start. Popup no-
+  connection message directs users to this section.
 
-- bpo-30642: Fix reference leaks in IDLE tests.
-  Patches by Louie Lu and Terry Jan Reedy.
+- bpo-30642: Fix reference leaks in IDLE tests. Patches by Louie Lu and
+  Terry Jan Reedy.
 
-- bpo-30495: Add docstrings for textview.py and use PEP8 names.
-  Patches by Cheryl Sabella and Terry Jan Reedy.
+- bpo-30495: Add docstrings for textview.py and use PEP8 names. Patches by
+  Cheryl Sabella and Terry Jan Reedy.
 
-- bpo-30290: Help-about: use pep8 names and add tests.
-  Increase coverage to 100%.
-  Patches by Louie Lu, Cheryl Sabella, and Terry Jan Reedy.
+- bpo-30290: Help-about: use pep8 names and add tests. Increase coverage to
+  100%. Patches by Louie Lu, Cheryl Sabella, and Terry Jan Reedy.
 
-- bpo-30303: Add _utest option to textview; add new tests.
-  Increase coverage to 100%.
-  Patches by Louie Lu and Terry Jan Reedy.
+- bpo-30303: Add _utest option to textview; add new tests. Increase coverage
+  to 100%. Patches by Louie Lu and Terry Jan Reedy.
 
 C API
 -----
 
-- Issue #27867: Function PySlice_GetIndicesEx() no longer replaced with a macro
+- bpo-27867: Function PySlice_GetIndicesEx() no longer replaced with a macro
   if Py_LIMITED_API is not set.
 
-
 Build
 -----
 
-- bpo-29941: Add ``--with-assertions`` configure flag to explicitly enable
-  C ``assert()`` checks. Defaults to off. ``--with-pydebug`` implies
-  ``--with-assertions``.
+- bpo-29941: Add ``--with-assertions`` configure flag to explicitly enable C
+  ``assert()`` checks. Defaults to off. ``--with-pydebug`` implies ``--with-
+  assertions``.
 
-- bpo-28787: Fix out-of-tree builds of Python when configured with
-  ``--with--dtrace``.
+- bpo-28787: Fix out-of-tree builds of Python when configured with ``--with
+  --dtrace``.
 
 - bpo-29243: Prevent unnecessary rebuilding of Python during ``make test``,
   ``make install`` and some other make targets when configured with
-  ``--enable-optimizations``.
+  ``--enable- optimizations``.
 
-- bpo-23404: Don't regenerate generated files based on file modification time
-  anymore: the action is now explicit. Replace ``make touch`` with
+- bpo-23404: Don't regenerate generated files based on file modification
+  time anymore: the action is now explicit. Replace ``make touch`` with
   ``make regen-all``.
 
 - bpo-29643: Fix ``--enable-optimization`` didn't work.
@@ -328,12 +838,13 @@ Build
 Documentation
 -------------
 
-- bpo-30176: Add missing attribute related constants in curses documentation.
+- bpo-30176: Add missing attribute related constants in curses
+  documentation.
 
-- Issue #30052: the link targets for :func:`bytes` and
-  :func:`bytearray` are now their respective type definitions, rather
-  than the corresponding builtin function entries. Use :ref:`bytes <func-bytes>`
-  and :ref:`bytearray <func-bytearray>` to reference the latter.
+- bpo-30052: the link targets for :func:`bytes` and :func:`bytearray` are
+  now their respective type definitions, rather than the corresponding
+  builtin function entries. Use :ref:`bytes <func-bytes>` and
+  :ref:`bytearray <func-bytearray>` to reference the latter.
 
   In order to ensure this and future cross-reference updates are applied
   automatically, the daily documentation builds now disable the default
@@ -344,22 +855,22 @@ Documentation
 Tools/Demos
 -----------
 
-- Issue #29367: python-gdb.py now supports also ``method-wrapper``
+- bpo-29367: python-gdb.py now supports also ``method-wrapper``
   (``wrapperobject``) objects.
 
 Tests
 -----
 
 - bpo-30357: test_thread: setUp() now uses support.threading_setup() and
-  support.threading_cleanup() to wait until threads complete to avoid
-  random side effects on following tests. Initial patch written by Grzegorz
+  support.threading_cleanup() to wait until threads complete to avoid random
+  side effects on following tests. Initial patch written by Grzegorz
   Grzywacz.
 
 - bpo-30197: Enhanced functions swap_attr() and swap_item() in the
-  test.support module.  They now work when delete replaced attribute or item
-  inside the with statement.  The old value of the attribute or item (or None
-  if it doesn't exist) now will be assigned to the target of the "as" clause,
-  if there is one.
+  test.support module. They now work when delete replaced attribute or item
+  inside the with statement.  The old value of the attribute or item (or
+  None if it doesn't exist) now will be assigned to the target of the "as"
+  clause, if there is one.
 
 Windows
 -------
@@ -368,26 +879,26 @@ Windows
   vcvarsall.bat
 
 - bpo-30450: The build process on Windows no longer depends on Subversion,
-  instead pulling external code from GitHub via a Python script.  If Python 3.6
-  is not found on the system (via ``py -3.6``), NuGet is used to download a
-  copy of 32-bit Python.
+  instead pulling external code from GitHub via a Python script.  If Python
+  3.6 is not found on the system (via ``py -3.6``), NuGet is used to
+  download a copy of 32-bit Python.
 
 
-What's New in Python 3.6.1?
-===========================
+What's New in Python 3.6.1 final?
+=================================
 
 *Release date: 2017-03-21*
 
 Core and Builtins
 -----------------
 
-- bpo-29723: The ``sys.path[0]`` initialization change for bpo-29139 caused a
-  regression by revealing an inconsistency in how sys.path is initialized when
-  executing ``__main__`` from a zipfile, directory, or other import location.
-  The interpreter now consistently avoids ever adding the import location's
-  parent directory to ``sys.path``, and ensures no other ``sys.path`` entries
-  are inadvertently modified when inserting the import location named on the
-  command line.
+- bpo-29723: The ``sys.path[0]`` initialization change for bpo-29139 caused
+  a regression by revealing an inconsistency in how sys.path is initialized
+  when executing ``__main__`` from a zipfile, directory, or other import
+  location. The interpreter now consistently avoids ever adding the import
+  location's parent directory to ``sys.path``, and ensures no other
+  ``sys.path`` entries are inadvertently modified when inserting the import
+  location named on the command line.
 
 Build
 -----
@@ -397,8 +908,8 @@ Build
 - Fix incompatible comment in python.h
 
 
-What's New in Python 3.6.1 release candidate 1
-==============================================
+What's New in Python 3.6.1 release candidate 1?
+===============================================
 
 *Release date: 2017-03-04*
 
@@ -408,241 +919,240 @@ Core and Builtins
 - bpo-28893: Set correct __cause__ for errors about invalid awaitables
   returned from __aiter__ and __anext__.
 
-- bpo-29683: Fixes to memory allocation in _PyCode_SetExtra.  Patch by
-  Brian Coleman.
+- bpo-29683: Fixes to memory allocation in _PyCode_SetExtra.  Patch by Brian
+  Coleman.
 
-- bpo-29684: Fix minor regression of PyEval_CallObjectWithKeywords.
-  It should raise TypeError when kwargs is not a dict.  But it might
-  cause segv when args=NULL and kwargs is not a dict.
+- bpo-29684: Fix minor regression of PyEval_CallObjectWithKeywords. It
+  should raise TypeError when kwargs is not a dict.  But it might cause segv
+  when args=NULL and kwargs is not a dict.
 
-- Issue #28598: Support __rmod__ for subclasses of str being called before
-  str.__mod__.  Patch by Martijn Pieters.
+- bpo-28598: Support __rmod__ for subclasses of str being called before
+  str.__mod__. Patch by Martijn Pieters.
 
-- bpo-29607: Fix stack_effect computation for CALL_FUNCTION_EX.
-  Patch by Matthieu Dartiailh.
+- bpo-29607: Fix stack_effect computation for CALL_FUNCTION_EX. Patch by
+  Matthieu Dartiailh.
 
-- bpo-29602: Fix incorrect handling of signed zeros in complex constructor for
-  complex subclasses and for inputs having a __complex__ method. Patch
+- bpo-29602: Fix incorrect handling of signed zeros in complex constructor
+  for complex subclasses and for inputs having a __complex__ method. Patch
   by Serhiy Storchaka.
 
-- bpo-29347: Fixed possibly dereferencing undefined pointers
-  when creating weakref objects.
+- bpo-29347: Fixed possibly dereferencing undefined pointers when creating
+  weakref objects.
 
 - bpo-29438: Fixed use-after-free problem in key sharing dict.
 
-- Issue #29319: Prevent RunMainFromImporter overwriting sys.path[0].
+- bpo-29319: Prevent RunMainFromImporter overwriting sys.path[0].
 
-- Issue #29337: Fixed possible BytesWarning when compare the code objects.
+- bpo-29337: Fixed possible BytesWarning when compare the code objects.
   Warnings could be emitted at compile time.
 
-- Issue #29327: Fixed a crash when pass the iterable keyword argument to
+- bpo-29327: Fixed a crash when pass the iterable keyword argument to
   sorted().
 
-- Issue #29034: Fix memory leak and use-after-free in os module (path_converter).
+- bpo-29034: Fix memory leak and use-after-free in os module
+  (path_converter).
 
-- Issue #29159: Fix regression in bytes(x) when x.__index__() raises Exception.
+- bpo-29159: Fix regression in bytes(x) when x.__index__() raises Exception.
 
-- Issue #28932: Do not include <sys/random.h> if it does not exist.
+- bpo-28932: Do not include <sys/random.h> if it does not exist.
 
-- Issue #25677: Correct the positioning of the syntax error caret for
-  indented blocks.  Based on patch by Michael Layzell.
+- bpo-25677: Correct the positioning of the syntax error caret for indented
+  blocks. Based on patch by Michael Layzell.
 
-- Issue #29000: Fixed bytes formatting of octals with zero padding in alternate
+- bpo-29000: Fixed bytes formatting of octals with zero padding in alternate
   form.
 
-- Issue #26919: On Android, operating system data is now always encoded/decoded
-  to/from UTF-8, instead of the locale encoding to avoid inconsistencies with
-  os.fsencode() and os.fsdecode() which are already using UTF-8.
+- bpo-26919: On Android, operating system data is now always encoded/decoded
+  to/from UTF-8, instead of the locale encoding to avoid inconsistencies
+  with os.fsencode() and os.fsdecode() which are already using UTF-8.
 
-- Issue #28991:  functools.lru_cache() was susceptible to an obscure reentrancy
+- bpo-28991: functools.lru_cache() was susceptible to an obscure reentrancy
   bug triggerable by a monkey-patched len() function.
 
-- Issue #28739: f-string expressions are no longer accepted as docstrings and
+- bpo-28739: f-string expressions are no longer accepted as docstrings and
   by ast.literal_eval() even if they do not include expressions.
 
-- Issue #28512: Fixed setting the offset attribute of SyntaxError by
+- bpo-28512: Fixed setting the offset attribute of SyntaxError by
   PyErr_SyntaxLocationEx() and PyErr_SyntaxLocationObject().
 
-- Issue #28918: Fix the cross compilation of xxlimited when Python has been
+- bpo-28918: Fix the cross compilation of xxlimited when Python has been
   built with Py_DEBUG defined.
 
-- Issue #28731: Optimize _PyDict_NewPresized() to create correct size dict.
+- bpo-28731: Optimize _PyDict_NewPresized() to create correct size dict.
   Improve speed of dict literal with constant keys up to 30%.
 
-Extension Modules
------------------
-
-- Issue #29169: Update zlib to 1.2.11.
-
 Library
 -------
 
+- bpo-29169: Update zlib to 1.2.11.
+
 - bpo-29623: Allow use of path-like object as a single argument in
-  ConfigParser.read().  Patch by David Ellis.
+  ConfigParser.read(). Patch by David Ellis.
 
-- bpo-28963: Fix out of bound iteration in asyncio.Future.remove_done_callback
-  implemented in C.
+- bpo-28963: Fix out of bound iteration in
+  asyncio.Future.remove_done_callback implemented in C.
 
 - bpo-29704: asyncio.subprocess.SubprocessStreamProtocol no longer closes
   before all pipes are closed.
 
-- bpo-29271: Fix Task.current_task and Task.all_tasks implemented in C
-  to accept None argument as their pure Python implementation.
+- bpo-29271: Fix Task.current_task and Task.all_tasks implemented in C to
+  accept None argument as their pure Python implementation.
 
-- bpo-29703: Fix asyncio to support instantiation of new event loops
-  in child processes.
+- bpo-29703: Fix asyncio to support instantiation of new event loops in
+  child processes.
 
 - bpo-29376: Fix assertion error in threading._DummyThread.is_alive().
 
 - bpo-28624: Add a test that checks that cwd parameter of Popen() accepts
   PathLike objects.  Patch by Sayan Chowdhury.
 
-- bpo-28518: Start a transaction implicitly before a DML statement.
-  Patch by Aviv Palivoda.
+- bpo-28518: Start a transaction implicitly before a DML statement. Patch by
+  Aviv Palivoda.
 
-- bpo-29532: Altering a kwarg dictionary passed to functools.partial()
-  no longer affects a partial object after creation.
+- bpo-29532: Altering a kwarg dictionary passed to functools.partial() no
+  longer affects a partial object after creation.
 
 - bpo-29110: Fix file object leak in aifc.open() when file is given as a
   filesystem path and is not in valid AIFF format. Patch by Anthony Zhang.
 
-- Issue #28556: Various updates to typing module: typing.Counter, typing.ChainMap,
-  improved ABC caching, etc. Original PRs by Jelle Zijlstra, Ivan Levkivskyi,
-  Manuel Krebber, and Łukasz Langa.
+- bpo-28556: Various updates to typing module: typing.Counter,
+  typing.ChainMap, improved ABC caching, etc. Original PRs by Jelle
+  Zijlstra, Ivan Levkivskyi, Manuel Krebber, and Łukasz Langa.
 
-- Issue #29100: Fix datetime.fromtimestamp() regression introduced in Python
+- bpo-29100: Fix datetime.fromtimestamp() regression introduced in Python
   3.6.0: check minimum and maximum years.
 
-- Issue #29519: Fix weakref spewing exceptions during interpreter shutdown
-  when used with a rare combination of multiprocessing and custom codecs.
+- bpo-29519: Fix weakref spewing exceptions during interpreter shutdown when
+  used with a rare combination of multiprocessing and custom codecs.
 
-- Issue #29416: Prevent infinite loop in pathlib.Path.mkdir
+- bpo-29416: Prevent infinite loop in pathlib.Path.mkdir
 
-- Issue #29444: Fixed out-of-bounds buffer access in the group() method of
-  the match object.  Based on patch by WGH.
+- bpo-29444: Fixed out-of-bounds buffer access in the group() method of the
+  match object. Based on patch by WGH.
 
-- Issue #29335: Fix subprocess.Popen.wait() when the child process has
-  exited to a stopped instead of terminated state (ex: when under ptrace).
+- bpo-29335: Fix subprocess.Popen.wait() when the child process has exited
+  to a stopped instead of terminated state (ex: when under ptrace).
 
-- Issue #29290: Fix a regression in argparse that help messages would wrap at
+- bpo-29290: Fix a regression in argparse that help messages would wrap at
   non-breaking spaces.
 
-- Issue #28735: Fixed the comparison of mock.MagickMock with mock.ANY.
+- bpo-28735: Fixed the comparison of mock.MagickMock with mock.ANY.
 
-- Issue #29316: Restore the provisional status of typing module, add
+- bpo-29316: Restore the provisional status of typing module, add
   corresponding note to documentation. Patch by Ivan L.
 
-- Issue #29219: Fixed infinite recursion in the repr of uninitialized
+- bpo-29219: Fixed infinite recursion in the repr of uninitialized
   ctypes.CDLL instances.
 
-- Issue #29011:  Fix an important omission by adding Deque to the typing module.
+- bpo-29011: Fix an important omission by adding Deque to the typing module.
 
-- Issue #28969: Fixed race condition in C implementation of functools.lru_cache.
-  KeyError could be raised when cached function with full cache was
-  simultaneously called from differen threads with the same uncached arguments.
+- bpo-28969: Fixed race condition in C implementation of
+  functools.lru_cache. KeyError could be raised when cached function with
+  full cache was simultaneously called from differen threads with the same
+  uncached arguments.
 
-- Issue #29142: In urllib.request, suffixes in no_proxy environment variable with
-  leading dots could match related hostnames again (e.g. .b.c matches a.b.c).
-  Patch by Milan Oberkirch.
+- bpo-29142: In urllib.request, suffixes in no_proxy environment variable
+  with leading dots could match related hostnames again (e.g. .b.c matches
+  a.b.c). Patch by Milan Oberkirch.
 
-- Issue #28961: Fix unittest.mock._Call helper: don't ignore the name parameter
+- bpo-28961: Fix unittest.mock._Call helper: don't ignore the name parameter
   anymore. Patch written by Jiajun Huang.
 
-- Issue #29203:  functools.lru_cache() now respects PEP 468 and preserves
-  the order of keyword arguments.  f(a=1, b=2) is now cached separately
-  from f(b=2, a=1) since both calls could potentially give different results.
+- bpo-29203: functools.lru_cache() now respects PEP 468 and preserves the
+  order of keyword arguments.  f(a=1, b=2) is now cached separately from
+  f(b=2, a=1) since both calls could potentially give different results.
 
-- Issue #15812: inspect.getframeinfo() now correctly shows the first line of
-  a context.  Patch by Sam Breese.
+- bpo-15812: inspect.getframeinfo() now correctly shows the first line of a
+  context. Patch by Sam Breese.
 
-- Issue #29094: Offsets in a ZIP file created with extern file object and modes
+- bpo-29094: Offsets in a ZIP file created with extern file object and modes
   "w" and "x" now are relative to the start of the file.
 
-- Issue #29085: Allow random.Random.seed() to use high quality OS randomness
+- bpo-29085: Allow random.Random.seed() to use high quality OS randomness
   rather than the pid and time.
 
-- Issue #29061: Fixed bug in secrets.randbelow() which would hang when given
-  negative input.  Patch by Brendan Donegan.
+- bpo-29061: Fixed bug in secrets.randbelow() which would hang when given a
+  negative input.  Patch by Brendan Donegan.
 
-- Issue #29079: Prevent infinite loop in pathlib.resolve() on Windows
+- bpo-29079: Prevent infinite loop in pathlib.resolve() on Windows
 
-- Issue #13051: Fixed recursion errors in large or resized
+- bpo-13051: Fixed recursion errors in large or resized
   curses.textpad.Textbox.  Based on patch by Tycho Andersen.
 
-- Issue #29119: Fix weakrefs in the pure python version of
-  collections.OrderedDict move_to_end() method.
-  Contributed by Andra Bogildea.
+- bpo-29119: Fix weakrefs in the pure python version of
+  collections.OrderedDict move_to_end() method. Contributed by Andra
+  Bogildea.
 
-- Issue #9770: curses.ascii predicates now work correctly with negative
+- bpo-9770: curses.ascii predicates now work correctly with negative
   integers.
 
-- Issue #28427: old keys should not remove new values from
-  WeakValueDictionary when collecting from another thread.
+- bpo-28427: old keys should not remove new values from WeakValueDictionary
+  when collecting from another thread.
 
-- Issue #28923: Remove editor artifacts from Tix.py.
+- bpo-28923: Remove editor artifacts from Tix.py.
 
-- Issue #29055:  Neaten-up empty population error on random.choice()
-  by suppressing the upstream exception.
+- bpo-29055: Neaten-up empty population error on random.choice() by
+  suppressing the upstream exception.
 
-- Issue #28871: Fixed a crash when deallocate deep ElementTree.
+- bpo-28871: Fixed a crash when deallocate deep ElementTree.
 
-- Issue #19542: Fix bugs in WeakValueDictionary.setdefault() and
-  WeakValueDictionary.pop() when a GC collection happens in another
-  thread.
+- bpo-19542: Fix bugs in WeakValueDictionary.setdefault() and
+  WeakValueDictionary.pop() when a GC collection happens in another thread.
 
-- Issue #20191: Fixed a crash in resource.prlimit() when passing a sequence that
-  doesn't own its elements as limits.
+- bpo-20191: Fixed a crash in resource.prlimit() when passing a sequence
+  that doesn't own its elements as limits.
 
-- Issue #28779: multiprocessing.set_forkserver_preload() would crash the
-  forkserver process if a preloaded module instantiated some
-  multiprocessing objects such as locks.
+- bpo-28779: multiprocessing.set_forkserver_preload() would crash the
+  forkserver process if a preloaded module instantiated some multiprocessing
+  objects such as locks.
 
-- Issue #28847: dbm.dumb now supports reading read-only files and no longer
+- bpo-28847: dbm.dumb now supports reading read-only files and no longer
   writes the index file when it is not changed.
 
-- Issue #26937: The chown() method of the tarfile.TarFile class does not fail
+- bpo-26937: The chown() method of the tarfile.TarFile class does not fail
   now when the grp module cannot be imported, as for example on Android
   platforms.
 
 IDLE
 ----
 
-- Issue #29071: IDLE colors f-string prefixes (but not invalid ur prefixes).
+- bpo-29071: IDLE colors f-string prefixes (but not invalid ur prefixes).
 
-- Issue #28572: Add 10% to coverage of IDLE's test_configdialog.
-  Update and augment description of the configuration system.
+- bpo-28572: Add 10% to coverage of IDLE's test_configdialog. Update and
+  augment description of the configuration system.
 
 Windows
 -------
 
 - bpo-29579: Removes readme.txt from the installer
 
-- Issue #29326: Ignores blank lines in ._pth files (Patch by Alexey Izbyshev)
+- bpo-29326: Ignores blank lines in ._pth files (Patch by Alexey Izbyshev)
 
-- Issue #28164: Correctly handle special console filenames (patch by Eryk Sun)
+- bpo-28164: Correctly handle special console filenames (patch by Eryk Sun)
 
-- Issue #29409: Implement PEP 529 for io.FileIO (Patch by Eryk Sun)
+- bpo-29409: Implement PEP 529 for io.FileIO (Patch by Eryk Sun)
 
-- Issue #29392: Prevent crash when passing invalid arguments into msvcrt module.
+- bpo-29392: Prevent crash when passing invalid arguments into msvcrt
+  module.
 
-- Issue #25778: winreg does not truncate string correctly (Patch by Eryk Sun)
+- bpo-25778: winreg does not truncate string correctly (Patch by Eryk Sun)
 
-- Issue #28896: Deprecate WindowsRegistryFinder and disable it by default.
+- bpo-28896: Deprecate WindowsRegistryFinder and disable it by default.
 
 C API
 -----
 
-- Issue #27867: Function PySlice_GetIndicesEx() is replaced with a macro if
-  Py_LIMITED_API is not set or set to the value between 0x03050400
-  and 0x03060000 (not including) or 0x03060100 or higher.
+- bpo-27867: Function PySlice_GetIndicesEx() is replaced with a macro if
+  Py_LIMITED_API is not set or set to the value between 0x03050400 and
+  0x03060000 (not including) or 0x03060100 or higher.
 
-- Issue #29083: Fixed the declaration of some public API functions.
+- bpo-29083: Fixed the declaration of some public API functions.
   PyArg_VaParse() and PyArg_VaParseTupleAndKeywords() were not available in
-  limited API.  PyArg_ValidateKeywordArguments(), PyArg_UnpackTuple() and
+  limited API. PyArg_ValidateKeywordArguments(), PyArg_UnpackTuple() and
   Py_BuildValue() were not available in limited API of version < 3.3 when
   PY_SSIZE_T_CLEAN is defined.
 
-- Issue #29058: All stable API extensions added after Python 3.2 are now
+- bpo-29058: All stable API extensions added after Python 3.2 are now
   available only when Py_LIMITED_API is set to the PY_VERSION_HEX value of
   the minimum Python version supporting this API.
 
@@ -651,73 +1161,75 @@ Documentation
 
 - bpo-28929: Link the documentation to its source file on GitHub.
 
-- bpo-25008: Document smtpd.py as effectively deprecated and add a pointer to
-  aiosmtpd, a third-party asyncio-based replacement.
+- bpo-25008: Document smtpd.py as effectively deprecated and add a pointer
+  to aiosmtpd, a third-party asyncio-based replacement.
 
-- Issue #26355: Add canonical header link on each page to corresponding major
+- bpo-26355: Add canonical header link on each page to corresponding major
   version of the documentation. Patch by Matthias Bussonnier.
 
-- Issue #29349: Fix Python 2 syntax in code for building the documentation.
+- bpo-29349: Fix Python 2 syntax in code for building the documentation.
 
 Tests
 -----
 
-- bpo-28087: Skip test_asyncore and test_eintr poll failures on macOS.
-  Skip some tests of select.poll when running on macOS due to unresolved
-  issues with the underlying system poll function on some macOS versions.
+- bpo-28087: Skip test_asyncore and test_eintr poll failures on macOS. Skip
+  some tests of select.poll when running on macOS due to unresolved issues
+  with the underlying system poll function on some macOS versions.
 
-- Issue #29571: to match the behaviour of the ``re.LOCALE`` flag,
-  test_re.test_locale_flag now uses ``locale.getpreferredencoding(False)`` to
-  determine the candidate encoding for the test regex (allowing it to correctly
-  skip the test when the default locale encoding is a multi-byte encoding)
+- bpo-29571: to match the behaviour of the ``re.LOCALE`` flag,
+  test_re.test_locale_flag now uses ``locale.getpreferredencoding(False)``
+  to determine the candidate encoding for the test regex (allowing it to
+  correctly skip the test when the default locale encoding is a multi-byte
+  encoding)
 
-- Issue #28950: Disallow -j0 to be combined with -T/-l in regrtest
-  command line arguments.
+- bpo-28950: Disallow -j0 to be combined with -T/-l in regrtest command line
+  arguments.
 
-- Issue #28683: Fix the tests that bind() a unix socket and raise
+- bpo-28683: Fix the tests that bind() a unix socket and raise
   PermissionError on Android for a non-root user.
 
-- Issue #26939: Add the support.setswitchinterval() function to fix
+- bpo-26939: Add the support.setswitchinterval() function to fix
   test_functools hanging on the Android armv7 qemu emulator.
 
 Build
 -----
 
 - bpo-27593: sys.version and the platform module python_build(),
-  python_branch(), and python_revision() functions now use
-  git information rather than hg when building from a repo.
+  python_branch(), and python_revision() functions now use git information
+  rather than hg when building from a repo.
 
 - bpo-29572: Update Windows build and OS X installers to use OpenSSL 1.0.2k.
 
-- Issue #26851: Set Android compilation and link flags.
+- bpo-26851: Set Android compilation and link flags.
 
-- Issue #28768: Fix implicit declaration of function _setmode. Patch by
+- bpo-28768: Fix implicit declaration of function _setmode. Patch by
   Masayuki Yamamoto
 
-- Issue #29080: Removes hard dependency on hg.exe from PCBuild/build.bat
+- bpo-29080: Removes hard dependency on hg.exe from PCBuild/build.bat
 
-- Issue #23903: Added missed names to PC/python3.def.
+- bpo-23903: Added missed names to PC/python3.def.
 
-- Issue #28762: lockf() is available on Android API level 24, but the F_LOCK
+- bpo-28762: lockf() is available on Android API level 24, but the F_LOCK
   macro is not defined in android-ndk-r13.
 
-- Issue #28538: Fix the compilation error that occurs because if_nameindex() is
+- bpo-28538: Fix the compilation error that occurs because if_nameindex() is
   available on Android API level 24, but the if_nameindex structure is not
   defined.
 
-- Issue #20211: Do not add the directory for installing C header files and the
+- bpo-20211: Do not add the directory for installing C header files and the
   directory for installing object code libraries to the cross compilation
   search paths. Original patch by Thomas Petazzoni.
 
-- Issue #28849: Do not define sys.implementation._multiarch on Android.
+- bpo-28849: Do not define sys.implementation._multiarch on Android.
 
 
-What's New in Python 3.6.0?
-===========================
+What's New in Python 3.6.0 final?
+=================================
 
 *Release date: 2016-12-23*
 
-- No changes since release candidate 2
+No changes since release candidate 2
+
 
 
 What's New in Python 3.6.0 release candidate 2?
@@ -728,27 +1240,26 @@ What's New in Python 3.6.0 release candidate 2?
 Core and Builtins
 -----------------
 
-- Issue #28147: Fix a memory leak in split-table dictionaries: setattr()
-  must not convert combined table into split table. Patch written by INADA
-  Naoki.
+- bpo-28147: Fix a memory leak in split-table dictionaries: setattr() must
+  not convert combined table into split table. Patch written by INADA Naoki.
 
-- Issue #28990: Fix asyncio SSL hanging if connection is closed before
+- bpo-28990: Fix asyncio SSL hanging if connection is closed before
   handshake is completed. (Patch by HoHo-Ho)
 
 Tools/Demos
 -----------
 
-- Issue #28770: Fix python-gdb.py for fastcalls.
+- bpo-28770: Fix python-gdb.py for fastcalls.
 
 Windows
 -------
 
-- Issue #28896: Deprecate WindowsRegistryFinder.
+- bpo-28896: Deprecate WindowsRegistryFinder.
 
 Build
 -----
 
-- Issue #28898: Prevent gdb build errors due to HAVE_LONG_LONG redefinition.
+- bpo-28898: Prevent gdb build errors due to HAVE_LONG_LONG redefinition.
 
 
 What's New in Python 3.6.0 release candidate 1?
@@ -759,58 +1270,59 @@ What's New in Python 3.6.0 release candidate 1?
 Core and Builtins
 -----------------
 
-- Issue #23722: Rather than silently producing a class that doesn't support
+- bpo-23722: Rather than silently producing a class that doesn't support
   zero-argument ``super()`` in methods, failing to pass the new
   ``__classcell__`` namespace entry up to ``type.__new__`` now results in a
-  ``DeprecationWarning`` and a class that supports zero-argument ``super()``.
+  ``DeprecationWarning`` and a class that supports zero-argument
+  ``super()``.
 
-- Issue #28797: Modifying the class __dict__ inside the __set_name__ method of
+- bpo-28797: Modifying the class __dict__ inside the __set_name__ method of
   a descriptor that is used inside that class no longer prevents calling the
   __set_name__ method of other descriptors.
 
-- Issue #28782: Fix a bug in the implementation ``yield from`` when checking
-  if the next instruction is YIELD_FROM. Regression introduced by WORDCODE
+- bpo-28782: Fix a bug in the implementation ``yield from`` when checking if
+  the next instruction is YIELD_FROM. Regression introduced by WORDCODE
   (issue #26647).
 
 Library
 -------
 
-- Issue #27030: Unknown escapes in re.sub() replacement template are allowed
+- bpo-27030: Unknown escapes in re.sub() replacement template are allowed
   again.  But they still are deprecated and will be disabled in 3.7.
 
-- Issue #28835: Fix a regression introduced in warnings.catch_warnings():
-  call warnings.showwarning() if it was overriden inside the context manager.
+- bpo-28835: Fix a regression introduced in warnings.catch_warnings(): call
+  warnings.showwarning() if it was overriden inside the context manager.
 
-- Issue #27172: To assist with upgrades from 2.7, the previously documented
-  deprecation of ``inspect.getfullargspec()`` has been reversed. This decision
-  may be revisited again after the Python 2.7 branch is no longer officially
-  supported.
+- bpo-27172: To assist with upgrades from 2.7, the previously documented
+  deprecation of ``inspect.getfullargspec()`` has been reversed. This
+  decision may be revisited again after the Python 2.7 branch is no longer
+  officially supported.
 
-- Issue #26273: Add new :data:`socket.TCP_CONGESTION` (Linux 2.6.13) and
+- bpo-26273: Add new :data:`socket.TCP_CONGESTION` (Linux 2.6.13) and
   :data:`socket.TCP_USER_TIMEOUT` (Linux 2.6.37) constants. Patch written by
   Omar Sandoval.
 
-- Issue #24142: Reading a corrupt config file left configparser in an
-  invalid state.  Original patch by Florian Höch.
+- bpo-24142: Reading a corrupt config file left configparser in an invalid
+  state. Original patch by Florian Höch.
 
-- Issue #28843: Fix asyncio C Task to handle exceptions __traceback__.
+- bpo-28843: Fix asyncio C Task to handle exceptions __traceback__.
 
 C API
 -----
 
-- Issue #28808: PyUnicode_CompareWithASCIIString() now never raises exceptions.
+- bpo-28808: PyUnicode_CompareWithASCIIString() now never raises exceptions.
 
 Documentation
 -------------
 
-- Issue #23722: The data model reference and the porting section in the What's
+- bpo-23722: The data model reference and the porting section in the What's
   New guide now cover the additional ``__classcell__`` handling needed for
   custom metaclasses to fully support PEP 487 and zero-argument ``super()``.
 
 Tools/Demos
 -----------
 
-- Issue #28023: Fix python-gdb.py didn't support new dict implementation.
+- bpo-28023: Fix python-gdb.py didn't support new dict implementation.
 
 
 What's New in Python 3.6.0 beta 4?
@@ -821,111 +1333,109 @@ What's New in Python 3.6.0 beta 4?
 Core and Builtins
 -----------------
 
-- Issue #28532: Show sys.version when -V option is supplied twice.
+- bpo-28532: Show sys.version when -V option is supplied twice.
 
-- Issue #27100: The with-statement now checks for __enter__ before it
-  checks for __exit__.  This gives less confusing error messages when
-  both methods are missing. Patch by Jonathan Ellington.
+- bpo-27100: The with-statement now checks for __enter__ before it checks
+  for __exit__. This gives less confusing error messages when both methods
+  are missing. Patch by Jonathan Ellington.
 
-- Issue #28746: Fix the set_inheritable() file descriptor method on platforms
+- bpo-28746: Fix the set_inheritable() file descriptor method on platforms
   that do not have the ioctl FIOCLEX and FIONCLEX commands.
 
-- Issue #26920: Fix not getting the locale's charset upon initializing the
+- bpo-26920: Fix not getting the locale's charset upon initializing the
   interpreter, on platforms that do not have langinfo.
 
-- Issue #28648: Fixed crash in Py_DecodeLocale() in debug build on Mac OS X
+- bpo-28648: Fixed crash in Py_DecodeLocale() in debug build on Mac OS X
   when decode astral characters.  Patch by Xiang Zhang.
 
-- Issue #19398: Extra slash no longer added to sys.path components in case of
-  empty compile-time PYTHONPATH components.
+- bpo-19398: Extra slash no longer added to sys.path components in case of
+  empty compile- time PYTHONPATH components.
 
-- Issue #28665:  Improve speed of the STORE_DEREF opcode by 40%.
+- bpo-28665: Improve speed of the STORE_DEREF opcode by 40%.
 
-- Issue #28583: PyDict_SetDefault didn't combine split table when needed.
-  Patch by Xiang Zhang.
+- bpo-28583: PyDict_SetDefault didn't combine split table when needed. Patch
+  by Xiang Zhang.
 
-- Issue #27243: Change PendingDeprecationWarning -> DeprecationWarning.
-  As it was agreed in the issue, __aiter__ returning an awaitable
-  should result in PendingDeprecationWarning in 3.5 and in
-  DeprecationWarning in 3.6.
+- bpo-27243: Change PendingDeprecationWarning -> DeprecationWarning. As it
+  was agreed in the issue, __aiter__ returning an awaitable should result in
+  PendingDeprecationWarning in 3.5 and in DeprecationWarning in 3.6.
 
-- Issue #26182: Fix a refleak in code that raises DeprecationWarning.
+- bpo-26182: Fix a refleak in code that raises DeprecationWarning.
 
-- Issue #28721: Fix asynchronous generators aclose() and athrow() to
-  handle StopAsyncIteration propagation properly.
+- bpo-28721: Fix asynchronous generators aclose() and athrow() to handle
+  StopAsyncIteration propagation properly.
 
 Library
 -------
 
-- Issue #28752: Restored the __reduce__() methods of datetime objects.
+- bpo-28752: Restored the __reduce__() methods of datetime objects.
 
-- Issue #28727: Regular expression patterns, _sre.SRE_Pattern objects created
+- bpo-28727: Regular expression patterns, _sre.SRE_Pattern objects created
   by re.compile(), become comparable (only x==y and x!=y operators). This
-  change should fix the issue #18383: don't duplicate warning filters when the
-  warnings module is reloaded (thing usually only done in unit tests).
+  change should fix the issue #18383: don't duplicate warning filters when
+  the warnings module is reloaded (thing usually only done in unit tests).
 
-- Issue #20572: The subprocess.Popen.wait method's undocumented
-  endtime parameter now raises a DeprecationWarning.
+- bpo-20572: The subprocess.Popen.wait method's undocumented endtime
+  parameter now raises a DeprecationWarning.
 
-- Issue #25659: In ctypes, prevent a crash calling the from_buffer() and
+- bpo-25659: In ctypes, prevent a crash calling the from_buffer() and
   from_buffer_copy() methods on abstract classes like Array.
 
-- Issue #19717: Makes Path.resolve() succeed on paths that do not exist.
-  Patch by Vajrasky Kok
+- bpo-19717: Makes Path.resolve() succeed on paths that do not exist. Patch
+  by Vajrasky Kok
 
-- Issue #28563: Fixed possible DoS and arbitrary code execution when handle
+- bpo-28563: Fixed possible DoS and arbitrary code execution when handle
   plural form selections in the gettext module.  The expression parser now
   supports exact syntax supported by GNU gettext.
 
-- Issue #28387: Fixed possible crash in _io.TextIOWrapper deallocator when
-  the garbage collector is invoked in other thread.  Based on patch by
-  Sebastian Cufre.
+- bpo-28387: Fixed possible crash in _io.TextIOWrapper deallocator when the
+  garbage collector is invoked in other thread.  Based on patch by Sebastian
+  Cufre.
 
-- Issue #28600: Optimize loop.call_soon.
+- bpo-28600: Optimize loop.call_soon.
 
-- Issue #28613: Fix get_event_loop() return the current loop if
-  called from coroutines/callbacks.
+- bpo-28613: Fix get_event_loop() return the current loop if called from
+  coroutines/callbacks.
 
-- Issue #28634: Fix asyncio.isfuture() to support unittest.Mock.
+- bpo-28634: Fix asyncio.isfuture() to support unittest.Mock.
 
-- Issue #26081: Fix refleak in _asyncio.Future.__iter__().throw.
+- bpo-26081: Fix refleak in _asyncio.Future.__iter__().throw.
 
-- Issue #28639: Fix inspect.isawaitable to always return bool
-  Patch by Justin Mayfield.
+- bpo-28639: Fix inspect.isawaitable to always return bool Patch by Justin
+  Mayfield.
 
-- Issue #28652: Make loop methods reject socket kinds they do not support.
+- bpo-28652: Make loop methods reject socket kinds they do not support.
 
-- Issue #28653: Fix a refleak in functools.lru_cache.
+- bpo-28653: Fix a refleak in functools.lru_cache.
 
-- Issue #28703: Fix asyncio.iscoroutinefunction to handle Mock objects.
+- bpo-28703: Fix asyncio.iscoroutinefunction to handle Mock objects.
 
-- Issue #28704: Fix create_unix_server to support Path-like objects
-  (PEP 519).
+- bpo-28704: Fix create_unix_server to support Path-like objects (PEP 519).
 
-- Issue #28720: Add collections.abc.AsyncGenerator.
+- bpo-28720: Add collections.abc.AsyncGenerator.
 
 Documentation
 -------------
 
-- Issue #28513: Documented command-line interface of zipfile.
+- bpo-28513: Documented command-line interface of zipfile.
 
 Tests
 -----
 
-- Issue #28666: Now test.support.rmtree is able to remove unwritable or
+- bpo-28666: Now test.support.rmtree is able to remove unwritable or
   unreadable directories.
 
-- Issue #23839: Various caches now are cleared before running every test file.
+- bpo-23839: Various caches now are cleared before running every test file.
 
 Build
 -----
 
-- Issue #10656: Fix out-of-tree building on AIX.  Patch by Tristan Carel and
+- bpo-10656: Fix out-of-tree building on AIX.  Patch by Tristan Carel and
   Michael Haubenwallner.
 
-- Issue #26359: Rename --with-optimiations to --enable-optimizations.
+- bpo-26359: Rename --with-optimiations to --enable-optimizations.
 
-- Issue #28676: Prevent missing 'getentropy' declaration warning on macOS.
+- bpo-28676: Prevent missing 'getentropy' declaration warning on macOS.
   Patch by Gareth Rees.
 
 
@@ -937,123 +1447,120 @@ What's New in Python 3.6.0 beta 3?
 Core and Builtins
 -----------------
 
-- Issue #28128: Deprecation warning for invalid str and byte escape
-  sequences now prints better information about where the error
-  occurs. Patch by Serhiy Storchaka and Eric Smith.
+- bpo-28128: Deprecation warning for invalid str and byte escape sequences
+  now prints better information about where the error occurs. Patch by
+  Serhiy Storchaka and Eric Smith.
 
-- Issue #28509: dict.update() no longer allocate unnecessary large memory.
+- bpo-28509: dict.update() no longer allocate unnecessary large memory.
 
-- Issue #28426: Fixed potential crash in PyUnicode_AsDecodedObject() in debug
+- bpo-28426: Fixed potential crash in PyUnicode_AsDecodedObject() in debug
   build.
 
-- Issue #28517: Fixed of-by-one error in the peephole optimizer that caused
+- bpo-28517: Fixed of-by-one error in the peephole optimizer that caused
   keeping unreachable code.
 
-- Issue #28214: Improved exception reporting for problematic __set_name__
+- bpo-28214: Improved exception reporting for problematic __set_name__
   attributes.
 
-- Issue #23782: Fixed possible memory leak in _PyTraceback_Add() and exception
+- bpo-23782: Fixed possible memory leak in _PyTraceback_Add() and exception
   loss in PyTraceBack_Here().
 
-- Issue #28471: Fix "Python memory allocator called without holding the GIL"
+- bpo-28471: Fix "Python memory allocator called without holding the GIL"
   crash in socket.setblocking.
 
 Library
 -------
 
-- Issue #27517: LZMA compressor and decompressor no longer raise exceptions if
+- bpo-27517: LZMA compressor and decompressor no longer raise exceptions if
   given empty data twice.  Patch by Benjamin Fogle.
 
-- Issue #28549: Fixed segfault in curses's addch() with ncurses6.
+- bpo-28549: Fixed segfault in curses's addch() with ncurses6.
 
-- Issue #28449: tarfile.open() with mode "r" or "r:" now tries to open a tar
-  file with compression before trying to open it without compression.  Otherwise
-  it had 50% chance failed with ignore_zeros=True.
+- bpo-28449: tarfile.open() with mode "r" or "r:" now tries to open a tar
+  file with compression before trying to open it without compression.
+  Otherwise it had 50% chance failed with ignore_zeros=True.
 
-- Issue #23262: The webbrowser module now supports Firefox 36+ and derived
+- bpo-23262: The webbrowser module now supports Firefox 36+ and derived
   browsers.  Based on patch by Oleg Broytman.
 
-- Issue #27939: Fixed bugs in tkinter.ttk.LabeledScale and tkinter.Scale caused
+- bpo-27939: Fixed bugs in tkinter.ttk.LabeledScale and tkinter.Scale caused
   by representing the scale as float value internally in Tk.  tkinter.IntVar
   now works if float value is set to underlying Tk variable.
 
-- Issue #18844: The various ways of specifying weights for random.choices()
-  now produce the same result sequences.
+- bpo-18844: The various ways of specifying weights for random.choices() now
+  produce the same result sequences.
 
-- Issue #28255: calendar.TextCalendar().prmonth() no longer prints a space
-  at the start of new line after printing a month's calendar.  Patch by
-  Xiang Zhang.
+- bpo-28255: calendar.TextCalendar().prmonth() no longer prints a space at
+  the start of new line after printing a month's calendar.  Patch by Xiang
+  Zhang.
 
-- Issue #20491: The textwrap.TextWrapper class now honors non-breaking spaces.
+- bpo-20491: The textwrap.TextWrapper class now honors non-breaking spaces.
   Based on patch by Kaarle Ritvanen.
 
-- Issue #28353: os.fwalk() no longer fails on broken links.
+- bpo-28353: os.fwalk() no longer fails on broken links.
 
-- Issue #28430: Fix iterator of C implemented asyncio.Future doesn't accept
+- bpo-28430: Fix iterator of C implemented asyncio.Future doesn't accept
   non-None value is passed to it.send(val).
 
-- Issue #27025: Generated names for Tkinter widgets now start by the "!" prefix
+- bpo-27025: Generated names for Tkinter widgets now start by the "!" prefix
   for readability.
 
-- Issue #25464: Fixed HList.header_exists() in tkinter.tix module by addin
-  workaround to Tix library bug.
+- bpo-25464: Fixed HList.header_exists() in tkinter.tix module by addin a
+  workaround to Tix library bug.
 
-- Issue #28488: shutil.make_archive() no longer adds entry "./" to ZIP archive.
+- bpo-28488: shutil.make_archive() no longer adds entry "./" to ZIP archive.
 
-- Issue #25953: re.sub() now raises an error for invalid numerical group
-  reference in replacement template even if the pattern is not found in
-  the string.  Error message for invalid group reference now includes the
-  group index and the position of the reference.
-  Based on patch by SilentGhost.
+- bpo-25953: re.sub() now raises an error for invalid numerical group
+  reference in replacement template even if the pattern is not found in the
+  string.  Error message for invalid group reference now includes the group
+  index and the position of the reference. Based on patch by SilentGhost.
 
-- Issue #18219: Optimize csv.DictWriter for large number of columns.
-  Patch by Mariatta Wijaya.
+- bpo-18219: Optimize csv.DictWriter for large number of columns. Patch by
+  Mariatta Wijaya.
 
-- Issue #28448: Fix C implemented asyncio.Future didn't work on Windows.
+- bpo-28448: Fix C implemented asyncio.Future didn't work on Windows.
 
-- Issue #28480: Fix error building socket module when multithreading is
+- bpo-28480: Fix error building socket module when multithreading is
   disabled.
 
-- Issue #24452: Make webbrowser support Chrome on Mac OS X.
+- bpo-24452: Make webbrowser support Chrome on Mac OS X.
 
-- Issue #20766: Fix references leaked by pdb in the handling of SIGINT
+- bpo-20766: Fix references leaked by pdb in the handling of SIGINT
   handlers.
 
-- Issue #28492: Fix how StopIteration exception is raised in _asyncio.Future.
+- bpo-28492: Fix how StopIteration exception is raised in _asyncio.Future.
 
-- Issue #28500: Fix asyncio to handle async gens GC from another thread.
+- bpo-28500: Fix asyncio to handle async gens GC from another thread.
 
-- Issue #26923: Fix asyncio.Gather to refuse being cancelled once all
-  children are done.
-  Patch by Johannes Ebke.
+- bpo-26923: Fix asyncio.Gather to refuse being cancelled once all children
+  are done. Patch by Johannes Ebke.
 
-- Issue #26796: Don't configure the number of workers for default
-  threadpool executor.
-  Initial patch by Hans Lawrenz.
+- bpo-26796: Don't configure the number of workers for default threadpool
+  executor. Initial patch by Hans Lawrenz.
 
-- Issue #28544: Implement asyncio.Task in C.
+- bpo-28544: Implement asyncio.Task in C.
 
 Windows
 -------
 
-- Issue #28522: Fixes mishandled buffer reallocation in getpathp.c
+- bpo-28522: Fixes mishandled buffer reallocation in getpathp.c
 
 Build
 -----
 
-- Issue #28444: Fix missing extensions modules when cross compiling.
+- bpo-28444: Fix missing extensions modules when cross compiling.
 
-- Issue #28208: Update Windows build and OS X installers to use SQLite 3.14.2.
+- bpo-28208: Update Windows build and OS X installers to use SQLite 3.14.2.
 
-- Issue #28248: Update Windows build and OS X installers to use OpenSSL 1.0.2j.
+- bpo-28248: Update Windows build and OS X installers to use OpenSSL 1.0.2j.
 
 Tests
 -----
 
-- Issue #26944: Fix test_posix for Android where 'id -G' is entirely wrong or
+- bpo-26944: Fix test_posix for Android where 'id -G' is entirely wrong or
   missing the effective gid.
 
-- Issue #28409: regrtest: fix the parser of command line arguments.
+- bpo-28409: regrtest: fix the parser of command line arguments.
 
 
 What's New in Python 3.6.0 beta 2?
@@ -1064,268 +1571,267 @@ What's New in Python 3.6.0 beta 2?
 Core and Builtins
 -----------------
 
-- Issue #28183: Optimize and cleanup dict iteration.
+- bpo-28183: Optimize and cleanup dict iteration.
 
-- Issue #26081: Added C implementation of asyncio.Future.
-  Original patch by Yury Selivanov.
+- bpo-26081: Added C implementation of asyncio.Future. Original patch by
+  Yury Selivanov.
 
-- Issue #28379: Added sanity checks and tests for PyUnicode_CopyCharacters().
+- bpo-28379: Added sanity checks and tests for PyUnicode_CopyCharacters().
   Patch by Xiang Zhang.
 
-- Issue #28376: The type of long range iterator is now registered as Iterator.
+- bpo-28376: The type of long range iterator is now registered as Iterator.
   Patch by Oren Milman.
 
-- Issue #28376: Creating instances of range_iterator by calling range_iterator
+- bpo-28376: Creating instances of range_iterator by calling range_iterator
   type now is deprecated.  Patch by Oren Milman.
 
-- Issue #28376: The constructor of range_iterator now checks that step is not 0.
-  Patch by Oren Milman.
+- bpo-28376: The constructor of range_iterator now checks that step is not
+  0. Patch by Oren Milman.
 
-- Issue #26906: Resolving special methods of uninitialized type now causes
+- bpo-26906: Resolving special methods of uninitialized type now causes
   implicit initialization of the type instead of a fail.
 
-- Issue #18287: PyType_Ready() now checks that tp_name is not NULL.
-  Original patch by Niklas Koep.
+- bpo-18287: PyType_Ready() now checks that tp_name is not NULL. Original
+  patch by Niklas Koep.
 
-- Issue #24098: Fixed possible crash when AST is changed in process of
+- bpo-24098: Fixed possible crash when AST is changed in process of
   compiling it.
 
-- Issue #28201: Dict reduces possibility of 2nd conflict in hash table when
+- bpo-28201: Dict reduces possibility of 2nd conflict in hash table when
   hashes have same lower bits.
 
-- Issue #28350: String constants with null character no longer interned.
+- bpo-28350: String constants with null character no longer interned.
 
-- Issue #26617: Fix crash when GC runs during weakref callbacks.
+- bpo-26617: Fix crash when GC runs during weakref callbacks.
 
-- Issue #27942: String constants now interned recursively in tuples and frozensets.
+- bpo-27942: String constants now interned recursively in tuples and
+  frozensets.
 
-- Issue #21578: Fixed misleading error message when ImportError called with
+- bpo-21578: Fixed misleading error message when ImportError called with
   invalid keyword args.
 
-- Issue #28203: Fix incorrect type in complex(1.0, {2:3}) error message.
-  Patch by Soumya Sharma.
+- bpo-28203: Fix incorrect type in complex(1.0, {2:3}) error message. Patch
+  by Soumya Sharma.
 
-- Issue #28086: Single var-positional argument of tuple subtype was passed
+- bpo-28086: Single var-positional argument of tuple subtype was passed
   unscathed to the C-defined function.  Now it is converted to exact tuple.
 
-- Issue #28214: Now __set_name__ is looked up on the class instead of the
+- bpo-28214: Now __set_name__ is looked up on the class instead of the
   instance.
 
-- Issue #27955: Fallback on reading /dev/urandom device when the getrandom()
+- bpo-27955: Fallback on reading /dev/urandom device when the getrandom()
   syscall fails with EPERM, for example when blocked by SECCOMP.
 
-- Issue #28192: Don't import readline in isolated mode.
+- bpo-28192: Don't import readline in isolated mode.
 
 - Upgrade internal unicode databases to Unicode version 9.0.0.
 
-- Issue #28131: Fix a regression in zipimport's compile_source().  zipimport
+- bpo-28131: Fix a regression in zipimport's compile_source().  zipimport
   should use the same optimization level as the interpreter.
 
-- Issue #28126: Replace Py_MEMCPY with memcpy(). Visual Studio can properly
+- bpo-28126: Replace Py_MEMCPY with memcpy(). Visual Studio can properly
   optimize memcpy().
 
-- Issue #28120: Fix dict.pop() for splitted dictionary when trying to remove a
+- bpo-28120: Fix dict.pop() for splitted dictionary when trying to remove a
   "pending key" (Not yet inserted in split-table). Patch by Xiang Zhang.
 
-- Issue #26182: Raise DeprecationWarning when async and await keywords are
-  used as variable/attribute/class/function name.
+- bpo-26182: Raise DeprecationWarning when async and await keywords are used
+  as variable/attribute/class/function name.
 
 Library
 -------
 
-- Issue #27998: Fixed bytes path support in os.scandir() on Windows.
-  Patch by Eryk Sun.
+- bpo-27998: Fixed bytes path support in os.scandir() on Windows. Patch by
+  Eryk Sun.
 
-- Issue #28317: The disassembler now decodes FORMAT_VALUE argument.
+- bpo-28317: The disassembler now decodes FORMAT_VALUE argument.
 
-- Issue #26293: Fixed writing ZIP files that starts not from the start of the
+- bpo-26293: Fixed writing ZIP files that starts not from the start of the
   file.  Offsets in ZIP file now are relative to the start of the archive in
   conforming to the specification.
 
-- Issue #28380: unittest.mock Mock autospec functions now properly support
+- bpo-28380: unittest.mock Mock autospec functions now properly support
   assert_called, assert_not_called, and assert_called_once.
 
-- Issue #27181 remove statistics.geometric_mean and defer until 3.7.
+- bpo-27181: remove statistics.geometric_mean and defer until 3.7.
 
-- Issue #28229: lzma module now supports pathlib.
+- bpo-28229: lzma module now supports pathlib.
 
-- Issue #28321: Fixed writing non-BMP characters with binary format in plistlib.
+- bpo-28321: Fixed writing non-BMP characters with binary format in
+  plistlib.
 
-- Issue #28225: bz2 module now supports pathlib.  Initial patch by Ethan Furman.
+- bpo-28225: bz2 module now supports pathlib.  Initial patch by Ethan
+  Furman.
 
-- Issue #28227: gzip now supports pathlib.  Patch by Ethan Furman.
+- bpo-28227: gzip now supports pathlib.  Patch by Ethan Furman.
 
-- Issue #27358: Optimized merging var-keyword arguments and improved error
+- bpo-27358: Optimized merging var-keyword arguments and improved error
   message when passing a non-mapping as a var-keyword argument.
 
-- Issue #28257: Improved error message when passing a non-iterable as
-  a var-positional argument.  Added opcode BUILD_TUPLE_UNPACK_WITH_CALL.
+- bpo-28257: Improved error message when passing a non-iterable as a var-
+  positional argument.  Added opcode BUILD_TUPLE_UNPACK_WITH_CALL.
 
-- Issue #28322: Fixed possible crashes when unpickle itertools objects from
+- bpo-28322: Fixed possible crashes when unpickle itertools objects from
   incorrect pickle data.  Based on patch by John Leitch.
 
-- Issue #28228: imghdr now supports pathlib.
+- bpo-28228: imghdr now supports pathlib.
 
-- Issue #28226: compileall now supports pathlib.
+- bpo-28226: compileall now supports pathlib.
 
-- Issue #28314: Fix function declaration (C flags) for the getiterator() method
+- bpo-28314: Fix function declaration (C flags) for the getiterator() method
   of xml.etree.ElementTree.Element.
 
-- Issue #28148: Stop using localtime() and gmtime() in the time
-  module.
+- bpo-28148: Stop using localtime() and gmtime() in the time module.
 
-  Introduced platform independent _PyTime_localtime API that is
-  similar to POSIX localtime_r, but available on all platforms.  Patch
-  by Ed Schouten.
+  Introduced platform independent _PyTime_localtime API that is similar to
+  POSIX localtime_r, but available on all platforms.  Patch by Ed Schouten.
 
-- Issue #28253: Fixed calendar functions for extreme months: 0001-01
-  and 9999-12.
+- bpo-28253: Fixed calendar functions for extreme months: 0001-01 and
+  9999-12.
 
-  Methods itermonthdays() and itermonthdays2() are reimplemented so
-  that they don't call itermonthdates() which can cause datetime.date
+  Methods itermonthdays() and itermonthdays2() are reimplemented so that
+  they don't call itermonthdates() which can cause datetime.date
   under/overflow.
 
-- Issue #28275: Fixed possible use after free in the decompress()
-  methods of the LZMADecompressor and BZ2Decompressor classes.
-  Original patch by John Leitch.
+- bpo-28275: Fixed possible use after free in the decompress() methods of
+  the LZMADecompressor and BZ2Decompressor classes. Original patch by John
+  Leitch.
 
-- Issue #27897: Fixed possible crash in sqlite3.Connection.create_collation()
+- bpo-27897: Fixed possible crash in sqlite3.Connection.create_collation()
   if pass invalid string-like object as a name.  Patch by Xiang Zhang.
 
-- Issue #18844: random.choices() now has k as a keyword-only argument
-  to improve the readability of common cases and come into line
-  with the signature used in other languages.
+- bpo-18844: random.choices() now has k as a keyword-only argument to
+  improve the readability of common cases and come into line with the
+  signature used in other languages.
 
-- Issue #18893: Fix invalid exception handling in Lib/ctypes/macholib/dyld.py.
+- bpo-18893: Fix invalid exception handling in Lib/ctypes/macholib/dyld.py.
   Patch by Madison May.
 
-- Issue #27611: Fixed support of default root window in the tkinter.tix module.
+- bpo-27611: Fixed support of default root window in the tkinter.tix module.
   Added the master parameter in the DisplayStyle constructor.
 
-- Issue #27348: In the traceback module, restore the formatting of exception
+- bpo-27348: In the traceback module, restore the formatting of exception
   messages like "Exception: None".  This fixes a regression introduced in
   3.5a2.
 
-- Issue #25651: Allow falsy values to be used for msg parameter of subTest().
+- bpo-25651: Allow falsy values to be used for msg parameter of subTest().
 
-- Issue #27778: Fix a memory leak in os.getrandom() when the getrandom() is
+- bpo-27778: Fix a memory leak in os.getrandom() when the getrandom() is
   interrupted by a signal and a signal handler raises a Python exception.
 
-- Issue #28200: Fix memory leak on Windows in the os module (fix
+- bpo-28200: Fix memory leak on Windows in the os module (fix
   path_converter() function).
 
-- Issue #25400: RobotFileParser now correctly returns default values for
+- bpo-25400: RobotFileParser now correctly returns default values for
   crawl_delay and request_rate.  Initial patch by Peter Wirtz.
 
-- Issue #27932: Prevent memory leak in win32_ver().
+- bpo-27932: Prevent memory leak in win32_ver().
 
 - Fix UnboundLocalError in socket._sendfile_use_sendfile.
 
-- Issue #28075: Check for ERROR_ACCESS_DENIED in Windows implementation of
+- bpo-28075: Check for ERROR_ACCESS_DENIED in Windows implementation of
   os.stat().  Patch by Eryk Sun.
 
-- Issue #22493: Warning message emitted by using inline flags in the middle of
-  regular expression now contains a (truncated) regex pattern.
-  Patch by Tim Graham.
+- bpo-22493: Warning message emitted by using inline flags in the middle of
+  regular expression now contains a (truncated) regex pattern. Patch by Tim
+  Graham.
 
-- Issue #25270: Prevent codecs.escape_encode() from raising SystemError when
-  an empty bytestring is passed.
+- bpo-25270: Prevent codecs.escape_encode() from raising SystemError when an
+  empty bytestring is passed.
 
-- Issue #28181: Get antigravity over HTTPS. Patch by Kaartic Sivaraam.
+- bpo-28181: Get antigravity over HTTPS. Patch by Kaartic Sivaraam.
 
-- Issue #25895: Enable WebSocket URL schemes in urllib.parse.urljoin.
-  Patch by Gergely Imreh and Markus Holtermann.
+- bpo-25895: Enable WebSocket URL schemes in urllib.parse.urljoin. Patch by
+  Gergely Imreh and Markus Holtermann.
 
-- Issue #28114: Fix a crash in parse_envlist() when env contains byte strings.
+- bpo-28114: Fix a crash in parse_envlist() when env contains byte strings.
   Patch by Eryk Sun.
 
-- Issue #27599: Fixed buffer overrun in binascii.b2a_qp() and binascii.a2b_qp().
+- bpo-27599: Fixed buffer overrun in binascii.b2a_qp() and
+  binascii.a2b_qp().
 
-- Issue #27906: Fix socket accept exhaustion during high TCP traffic.
-  Patch by Kevin Conway.
+- bpo-27906: Fix socket accept exhaustion during high TCP traffic. Patch by
+  Kevin Conway.
 
-- Issue #28174: Handle when SO_REUSEPORT isn't properly supported.
-  Patch by Seth Michael Larson.
+- bpo-28174: Handle when SO_REUSEPORT isn't properly supported. Patch by
+  Seth Michael Larson.
 
-- Issue #26654: Inspect functools.partial in asyncio.Handle.__repr__.
-  Patch by iceboy.
+- bpo-26654: Inspect functools.partial in asyncio.Handle.__repr__. Patch by
+  iceboy.
 
-- Issue #26909: Fix slow pipes IO in asyncio.
-  Patch by INADA Naoki.
+- bpo-26909: Fix slow pipes IO in asyncio. Patch by INADA Naoki.
 
-- Issue #28176: Fix callbacks race in asyncio.SelectorLoop.sock_connect.
+- bpo-28176: Fix callbacks race in asyncio.SelectorLoop.sock_connect.
 
-- Issue #27759: Fix selectors incorrectly retain invalid file descriptors.
+- bpo-27759: Fix selectors incorrectly retain invalid file descriptors.
   Patch by Mark Williams.
 
-- Issue #28368: Refuse monitoring processes if the child watcher has no
-  loop attached.
-  Patch by Vincent Michel.
+- bpo-28368: Refuse monitoring processes if the child watcher has no loop
+  attached. Patch by Vincent Michel.
 
-- Issue #28369: Raise RuntimeError when transport's FD is used with
-  add_reader, add_writer, etc.
+- bpo-28369: Raise RuntimeError when transport's FD is used with add_reader,
+  add_writer, etc.
 
-- Issue #28370: Speedup asyncio.StreamReader.readexactly.
-  Patch by Коренберг Марк.
+- bpo-28370: Speedup asyncio.StreamReader.readexactly. Patch by Коренберг
+  Марк.
 
-- Issue #28371: Deprecate passing asyncio.Handles to run_in_executor.
+- bpo-28371: Deprecate passing asyncio.Handles to run_in_executor.
 
-- Issue #28372: Fix asyncio to support formatting of non-python coroutines.
+- bpo-28372: Fix asyncio to support formatting of non-python coroutines.
 
-- Issue #28399: Remove UNIX socket from FS before binding.
-  Patch by Коренберг Марк.
+- bpo-28399: Remove UNIX socket from FS before binding. Patch by Коренберг
+  Марк.
 
-- Issue #27972: Prohibit Tasks to await on themselves.
+- bpo-27972: Prohibit Tasks to await on themselves.
 
 Windows
 -------
 
-- Issue #28402: Adds signed catalog files for stdlib on Windows.
+- bpo-28402: Adds signed catalog files for stdlib on Windows.
 
-- Issue #28333: Enables Unicode for ps1/ps2 and input() prompts. (Patch by
-  Eryk Sun)
+- bpo-28333: Enables Unicode for ps1/ps2 and input() prompts. (Patch by Eryk
+  Sun)
 
-- Issue #28251: Improvements to help manuals on Windows.
+- bpo-28251: Improvements to help manuals on Windows.
 
-- Issue #28110: launcher.msi has different product codes between 32-bit and
+- bpo-28110: launcher.msi has different product codes between 32-bit and
   64-bit
 
-- Issue #28161: Opening CON for write access fails
+- bpo-28161: Opening CON for write access fails
 
-- Issue #28162: WindowsConsoleIO readall() fails if first line starts with
+- bpo-28162: WindowsConsoleIO readall() fails if first line starts with
   Ctrl+Z
 
-- Issue #28163: WindowsConsoleIO fileno() passes wrong flags to
-  _open_osfhandle
+- bpo-28163: WindowsConsoleIO fileno() passes wrong flags to _open_osfhandle
 
-- Issue #28164: _PyIO_get_console_type fails for various paths
+- bpo-28164: _PyIO_get_console_type fails for various paths
 
-- Issue #28137: Renames Windows path file to ._pth
+- bpo-28137: Renames Windows path file to ._pth
 
-- Issue #28138: Windows ._pth file should allow import site
+- bpo-28138: Windows ._pth file should allow import site
 
 C API
 -----
 
-- Issue #28426: Deprecated undocumented functions PyUnicode_AsEncodedObject(),
+- bpo-28426: Deprecated undocumented functions PyUnicode_AsEncodedObject(),
   PyUnicode_AsDecodedObject(), PyUnicode_AsDecodedUnicode() and
   PyUnicode_AsEncodedUnicode().
 
 Build
 -----
 
-- Issue #28258: Fixed build with Estonian locale (python-config and distclean
+- bpo-28258: Fixed build with Estonian locale (python-config and distclean
   targets in Makefile).  Patch by Arfrever Frehtes Taifersar Arahesis.
 
-- Issue #26661: setup.py now detects system libffi with multiarch wrapper.
+- bpo-26661: setup.py now detects system libffi with multiarch wrapper.
 
-- Issue #15819: Remove redundant include search directory option for building
+- bpo-15819: Remove redundant include search directory option for building
   outside the source tree.
 
 Tests
 -----
 
-- Issue #28217: Adds _testconsole module to test console input.
+- bpo-28217: Adds _testconsole module to test console input.
 
 
 What's New in Python 3.6.0 beta 1?
@@ -1336,507 +1842,505 @@ What's New in Python 3.6.0 beta 1?
 Core and Builtins
 -----------------
 
-- Issue #23722: The __class__ cell used by zero-argument super() is now
-  initialized from type.__new__ rather than __build_class__, so class methods
-  relying on that will now work correctly when called from metaclass methods
-  during class creation. Patch by Martin Teichmann.
+- bpo-23722: The __class__ cell used by zero-argument super() is now
+  initialized from type.__new__ rather than __build_class__, so class
+  methods relying on that will now work correctly when called from metaclass
+  methods during class creation. Patch by Martin Teichmann.
 
-- Issue #25221: Fix corrupted result from PyLong_FromLong(0) when Python
-  is compiled with NSMALLPOSINTS = 0.
+- bpo-25221: Fix corrupted result from PyLong_FromLong(0) when Python is
+  compiled with NSMALLPOSINTS = 0.
 
-- Issue #27080: Implement formatting support for PEP 515.  Initial patch
-  by Chris Angelico.
+- bpo-27080: Implement formatting support for PEP 515.  Initial patch by
+  Chris Angelico.
 
-- Issue #27199: In tarfile, expose copyfileobj bufsize to improve throughput.
+- bpo-27199: In tarfile, expose copyfileobj bufsize to improve throughput.
   Patch by Jason Fried.
 
-- Issue #27948: In f-strings, only allow backslashes inside the braces
-  (where the expressions are).  This is a breaking change from the 3.6
-  alpha releases, where backslashes are allowed anywhere in an
-  f-string.  Also, require that expressions inside f-strings be
-  enclosed within literal braces, and not escapes like
-  ``f'\x7b"hi"\x7d'``.
+- bpo-27948: In f-strings, only allow backslashes inside the braces (where
+  the expressions are).  This is a breaking change from the 3.6 alpha
+  releases, where backslashes are allowed anywhere in an f-string.  Also,
+  require that expressions inside f-strings be enclosed within literal
+  braces, and not escapes like ``f'\x7b"hi"\x7d'``.
 
-- Issue #28046: Remove platform-specific directories from sys.path.
+- bpo-28046: Remove platform-specific directories from sys.path.
 
-- Issue #28071: Add early-out for differencing from an empty set.
+- bpo-28071: Add early-out for differencing from an empty set.
 
-- Issue #25758: Prevents zipimport from unnecessarily encoding a filename
+- bpo-25758: Prevents zipimport from unnecessarily encoding a filename
   (patch by Eryk Sun)
 
-- Issue #25856: The __module__ attribute of extension classes and functions
-  now is interned. This leads to more compact pickle data with protocol 4.
+- bpo-25856: The __module__ attribute of extension classes and functions now
+  is interned. This leads to more compact pickle data with protocol 4.
 
-- Issue #27213: Rework CALL_FUNCTION* opcodes to produce shorter and more
+- bpo-27213: Rework CALL_FUNCTION* opcodes to produce shorter and more
   efficient bytecode. Patch by Demur Rumed, design by Serhiy Storchaka,
   reviewed by Serhiy Storchaka and Victor Stinner.
 
-- Issue #26331: Implement tokenizing support for PEP 515. Patch by Georg Brandl.
+- bpo-26331: Implement tokenizing support for PEP 515. Patch by Georg
+  Brandl.
 
-- Issue #27999: Make "global after use" a SyntaxError, and ditto for nonlocal.
+- bpo-27999: Make "global after use" a SyntaxError, and ditto for nonlocal.
   Patch by Ivan Levkivskyi.
 
-- Issue #28003: Implement PEP 525 -- Asynchronous Generators.
+- bpo-28003: Implement PEP 525 -- Asynchronous Generators.
 
-- Issue #27985: Implement PEP 526 -- Syntax for Variable Annotations.
-  Patch by Ivan Levkivskyi.
+- bpo-27985: Implement PEP 526 -- Syntax for Variable Annotations. Patch by
+  Ivan Levkivskyi.
 
-- Issue #26058: Add a new private version to the builtin dict type, incremented
-  at each dictionary creation and at each dictionary change. Implementation of
-  the PEP 509.
+- bpo-26058: Add a new private version to the builtin dict type, incremented
+  at each dictionary creation and at each dictionary change. Implementation
+  of the PEP 509.
 
-- Issue #27364: A backslash-character pair that is not a valid escape sequence
+- bpo-27364: A backslash-character pair that is not a valid escape sequence
   now generates a DeprecationWarning.  Patch by Emanuel Barry.
 
-- Issue #27350: `dict` implementation is changed like PyPy. It is more compact
-  and preserves insertion order.
-  (Concept developed by Raymond Hettinger and patch by Inada Naoki.)
+- bpo-27350: `dict` implementation is changed like PyPy. It is more compact
+  and preserves insertion order. (Concept developed by Raymond Hettinger and
+  patch by Inada Naoki.)
 
-- Issue #27911: Remove unnecessary error checks in
+- bpo-27911: Remove unnecessary error checks in
   ``exec_builtin_or_dynamic()``.
 
-- Issue #27078: Added BUILD_STRING opcode.  Optimized f-strings evaluation.
+- bpo-27078: Added BUILD_STRING opcode.  Optimized f-strings evaluation.
 
-- Issue #17884: Python now requires systems with inttypes.h and stdint.h
+- bpo-17884: Python now requires systems with inttypes.h and stdint.h
 
-- Issue #27961: Require platforms to support ``long long``. Python hasn't
-  compiled without ``long long`` for years, so this is basically a formality.
+- bpo-27961: Require platforms to support ``long long``. Python hasn't
+  compiled without ``long long`` for years, so this is basically a
+  formality.
 
-- Issue #27355: Removed support for Windows CE.  It was never finished,
-  and Windows CE is no longer a relevant platform for Python.
+- bpo-27355: Removed support for Windows CE.  It was never finished, and
+  Windows CE is no longer a relevant platform for Python.
 
 - Implement PEP 523.
 
-- Issue #27870: A left shift of zero by a large integer no longer attempts
-  to allocate large amounts of memory.
+- bpo-27870: A left shift of zero by a large integer no longer attempts to
+  allocate large amounts of memory.
 
-- Issue #25402: In int-to-decimal-string conversion, improve the estimate
-  of the intermediate memory required, and remove an unnecessarily strict
+- bpo-25402: In int-to-decimal-string conversion, improve the estimate of
+  the intermediate memory required, and remove an unnecessarily strict
   overflow check. Patch by Serhiy Storchaka.
 
-- Issue #27214: In long_invert, be more careful about modifying object
-  returned by long_add, and remove an unnecessary check for small longs.
-  Thanks Oren Milman for analysis and patch.
+- bpo-27214: In long_invert, be more careful about modifying object returned
+  by long_add, and remove an unnecessary check for small longs. Thanks Oren
+  Milman for analysis and patch.
 
-- Issue #27506: Support passing the bytes/bytearray.translate() "delete"
+- bpo-27506: Support passing the bytes/bytearray.translate() "delete"
   argument by keyword.
 
-- Issue #27812: Properly clear out a generator's frame's backreference to the
+- bpo-27812: Properly clear out a generator's frame's backreference to the
   generator to prevent crashes in frame.clear().
 
-- Issue #27811: Fix a crash when a coroutine that has not been awaited is
+- bpo-27811: Fix a crash when a coroutine that has not been awaited is
   finalized with warnings-as-errors enabled.
 
-- Issue #27587: Fix another issue found by PVS-Studio: Null pointer check
-  after use of 'def' in _PyState_AddModule().
-  Initial patch by Christian Heimes.
+- bpo-27587: Fix another issue found by PVS-Studio: Null pointer check after
+  use of 'def' in _PyState_AddModule(). Initial patch by Christian Heimes.
 
-- Issue #27792: The modulo operation applied to ``bool`` and other
-  ``int`` subclasses now always returns an ``int``. Previously
-  the return type depended on the input values. Patch by Xiang Zhang.
+- bpo-27792: The modulo operation applied to ``bool`` and other ``int``
+  subclasses now always returns an ``int``. Previously the return type
+  depended on the input values. Patch by Xiang Zhang.
 
-- Issue #26984: int() now always returns an instance of exact int.
+- bpo-26984: int() now always returns an instance of exact int.
 
-- Issue #25604: Fix a minor bug in integer true division; this bug could
+- bpo-25604: Fix a minor bug in integer true division; this bug could
   potentially have caused off-by-one-ulp results on platforms with
   unreliable ldexp implementations.
 
-- Issue #24254: Make class definition namespace ordered by default.
+- bpo-24254: Make class definition namespace ordered by default.
 
-- Issue #27662: Fix an overflow check in ``List_New``: the original code was
+- bpo-27662: Fix an overflow check in ``List_New``: the original code was
   checking against ``Py_SIZE_MAX`` instead of the correct upper bound of
   ``Py_SSIZE_T_MAX``. Patch by Xiang Zhang.
 
-- Issue #27782: Multi-phase extension module import now correctly allows the
+- bpo-27782: Multi-phase extension module import now correctly allows the
   ``m_methods`` field to be used to add module level functions to instances
   of non-module types returned from ``Py_create_mod``. Patch by Xiang Zhang.
 
-- Issue #27936: The round() function accepted a second None argument
-  for some types but not for others.  Fixed the inconsistency by
-  accepting None for all numeric types.
+- bpo-27936: The round() function accepted a second None argument for some
+  types but not for others.  Fixed the inconsistency by accepting None for
+  all numeric types.
 
-- Issue #27487: Warn if a submodule argument to "python -m" or
+- bpo-27487: Warn if a submodule argument to "python -m" or
   runpy.run_module() is found in sys.modules after parent packages are
   imported, but before the submodule is executed.
 
-- Issue #27157: Make only type() itself accept the one-argument form.
-  Patch by Eryk Sun and Emanuel Barry.
+- bpo-27157: Make only type() itself accept the one-argument form. Patch by
+  Eryk Sun and Emanuel Barry.
 
-- Issue #27558: Fix a SystemError in the implementation of "raise" statement.
+- bpo-27558: Fix a SystemError in the implementation of "raise" statement.
   In a brand new thread, raise a RuntimeError since there is no active
   exception to reraise. Patch written by Xiang Zhang.
 
-- Issue #28008: Implement PEP 530 -- asynchronous comprehensions.
+- bpo-28008: Implement PEP 530 -- asynchronous comprehensions.
 
-- Issue #27942: Fix memory leak in codeobject.c
+- bpo-27942: Fix memory leak in codeobject.c
 
 Library
 -------
 
-- Issue #28732: Fix crash in os.spawnv() with no elements in args
+- bpo-28732: Fix crash in os.spawnv() with no elements in args
 
-- Issue #28485: Always raise ValueError for negative
+- bpo-28485: Always raise ValueError for negative
   compileall.compile_dir(workers=...) parameter, even when multithreading is
   unavailable.
 
-- Issue #28037: Use sqlite3_get_autocommit() instead of setting
+- bpo-28037: Use sqlite3_get_autocommit() instead of setting
   Connection->inTransaction manually.
 
-- Issue #25283: Attributes tm_gmtoff and tm_zone are now available on
-  all platforms in the return values of time.localtime() and
-  time.gmtime().
+- bpo-25283: Attributes tm_gmtoff and tm_zone are now available on all
+  platforms in the return values of time.localtime() and time.gmtime().
 
-- Issue #24454: Regular expression match object groups are now
-  accessible using __getitem__.  "mo[x]" is equivalent to
-  "mo.group(x)".
+- bpo-24454: Regular expression match object groups are now accessible using
+  __getitem__. "mo[x]" is equivalent to "mo.group(x)".
 
-- Issue #10740: sqlite3 no longer implicitly commit an open transaction
-  before DDL statements.
+- bpo-10740: sqlite3 no longer implicitly commit an open transaction before
+  DDL statements.
 
-- Issue #17941: Add a *module* parameter to collections.namedtuple().
+- bpo-17941: Add a *module* parameter to collections.namedtuple().
 
-- Issue #22493: Inline flags now should be used only at the start of the
-  regular expression.  Deprecation warning is emitted if uses them in the
+- bpo-22493: Inline flags now should be used only at the start of the
+  regular expression. Deprecation warning is emitted if uses them in the
   middle of the regular expression.
 
-- Issue #26885: xmlrpc now supports unmarshalling additional data types used
-  by Apache XML-RPC implementation for numerics and None.
+- bpo-26885: xmlrpc now supports unmarshalling additional data types used by
+  Apache XML- RPC implementation for numerics and None.
 
-- Issue #28070: Fixed parsing inline verbose flag in regular expressions.
+- bpo-28070: Fixed parsing inline verbose flag in regular expressions.
 
-- Issue #19500: Add client-side SSL session resumption to the ssl module.
+- bpo-19500: Add client-side SSL session resumption to the ssl module.
 
-- Issue #28022: Deprecate ssl-related arguments in favor of SSLContext. The
-  deprecation include manual creation of SSLSocket and certfile/keyfile
-  (or similar) in ftplib, httplib, imaplib, smtplib, poplib and urllib.
+- bpo-28022: Deprecate ssl-related arguments in favor of SSLContext. The
+  deprecation include manual creation of SSLSocket and certfile/keyfile (or
+  similar) in ftplib, httplib, imaplib, smtplib, poplib and urllib.
 
-- Issue #28043: SSLContext has improved default settings: OP_NO_SSLv2,
+- bpo-28043: SSLContext has improved default settings: OP_NO_SSLv2,
   OP_NO_SSLv3, OP_NO_COMPRESSION, OP_CIPHER_SERVER_PREFERENCE,
   OP_SINGLE_DH_USE, OP_SINGLE_ECDH_USE and HIGH ciphers without MD5.
 
-- Issue #24693: Changed some RuntimeError's in the zipfile module to more
+- bpo-24693: Changed some RuntimeError's in the zipfile module to more
   appropriate types. Improved some error messages and debugging output.
 
-- Issue #17909: ``json.load`` and ``json.loads`` now support binary input
+- bpo-17909: ``json.load`` and ``json.loads`` now support binary input
   encoded as UTF-8, UTF-16 or UTF-32. Patch by Serhiy Storchaka.
 
-- Issue #27137: the pure Python fallback implementation of ``functools.partial``
-  now matches the behaviour of its accelerated C counterpart for subclassing,
-  pickling and text representation purposes. Patch by Emanuel Barry and
-  Serhiy Storchaka.
+- bpo-27137: the pure Python fallback implementation of
+  ``functools.partial`` now matches the behaviour of its accelerated C
+  counterpart for subclassing, pickling and text representation purposes.
+  Patch by Emanuel Barry and Serhiy Storchaka.
 
 - Fix possible integer overflows and crashes in the mmap module with unusual
   usage patterns.
 
-- Issue #1703178: Fix the ability to pass the --link-objects option to the
+- bpo-1703178: Fix the ability to pass the --link-objects option to the
   distutils build_ext command.
 
-- Issue #28019: itertools.count() no longer rounds non-integer step in range
+- bpo-28019: itertools.count() no longer rounds non-integer step in range
   between 1.0 and 2.0 to 1.
 
-- Issue #18401: Pdb now supports the 'readrc' keyword argument to control
-  whether .pdbrc files should be read.  Patch by Martin Matusiak and
-  Sam Kimbrel.
+- bpo-18401: Pdb now supports the 'readrc' keyword argument to control
+  whether .pdbrc files should be read.  Patch by Martin Matusiak and Sam
+  Kimbrel.
 
-- Issue #25969: Update the lib2to3 grammar to handle the unpacking
+- bpo-25969: Update the lib2to3 grammar to handle the unpacking
   generalizations added in 3.5.
 
-- Issue #14977: mailcap now respects the order of the lines in the mailcap
+- bpo-14977: mailcap now respects the order of the lines in the mailcap
   files ("first match"), as required by RFC 1542.  Patch by Michael Lazar.
 
-- Issue #28082: Convert re flag constants to IntFlag.
+- bpo-28082: Convert re flag constants to IntFlag.
 
-- Issue #28025: Convert all ssl module constants to IntEnum and IntFlags.
+- bpo-28025: Convert all ssl module constants to IntEnum and IntFlags.
   SSLContext properties now return flags and enums.
 
-- Issue #23591: Add Flag, IntFlag, and auto() to enum module.
+- bpo-23591: Add Flag, IntFlag, and auto() to enum module.
 
-- Issue #433028: Added support of modifier spans in regular expressions.
+- bpo-433028: Added support of modifier spans in regular expressions.
 
-- Issue #24594: Validates persist parameter when opening MSI database
+- bpo-24594: Validates persist parameter when opening MSI database
 
-- Issue #17582: xml.etree.ElementTree nows preserves whitespaces in attributes
+- bpo-17582: xml.etree.ElementTree nows preserves whitespaces in attributes
   (Patch by Duane Griffin.  Reviewed and approved by Stefan Behnel.)
 
-- Issue #28047: Fixed calculation of line length used for the base64 CTE
-  in the new email policies.
+- bpo-28047: Fixed calculation of line length used for the base64 CTE in the
+  new email policies.
 
-- Issue #27576: Fix call order in OrderedDict.__init__().
+- bpo-27576: Fix call order in OrderedDict.__init__().
 
 - email.generator.DecodedGenerator now supports the policy keyword.
 
-- Issue #28027: Remove undocumented modules from ``Lib/plat-*``: IN, CDROM,
+- bpo-28027: Remove undocumented modules from ``Lib/plat-*``: IN, CDROM,
   DLFCN, TYPES, CDIO, and STROPTS.
 
-- Issue #27445: Don't pass str(_charset) to MIMEText.set_payload().
-  Patch by Claude Paroz.
+- bpo-27445: Don't pass str(_charset) to MIMEText.set_payload(). Patch by
+  Claude Paroz.
 
-- Issue #24277: The new email API is no longer provisional, and the docs
-  have been reorganized and rewritten to emphasize the new API.
+- bpo-24277: The new email API is no longer provisional, and the docs have
+  been reorganized and rewritten to emphasize the new API.
 
-- Issue #22450: urllib now includes an ``Accept: */*`` header among the
-  default headers.  This makes the results of REST API requests more
-  consistent and predictable especially when proxy servers are involved.
+- bpo-22450: urllib now includes an ``Accept: */*`` header among the default
+  headers. This makes the results of REST API requests more consistent and
+  predictable especially when proxy servers are involved.
 
 - lib2to3.pgen3.driver.load_grammar() now creates a stable cache file
   between runs given the same Grammar.txt input regardless of the hash
   randomization setting.
 
-- Issue #28005: Allow ImportErrors in encoding implementation to propagate.
+- bpo-28005: Allow ImportErrors in encoding implementation to propagate.
 
-- Issue #26667: Support path-like objects in importlib.util.
+- bpo-26667: Support path-like objects in importlib.util.
 
-- Issue #27570: Avoid zero-length memcpy() etc calls with null source
-  pointers in the "ctypes" and "array" modules.
+- bpo-27570: Avoid zero-length memcpy() etc calls with null source pointers
+  in the "ctypes" and "array" modules.
 
-- Issue #22233: Break email header lines *only* on the RFC specified CR and LF
-  characters, not on arbitrary unicode line breaks.  This also fixes a bug in
-  HTTP header parsing.
+- bpo-22233: Break email header lines *only* on the RFC specified CR and LF
+  characters, not on arbitrary unicode line breaks.  This also fixes a bug
+  in HTTP header parsing.
 
-- Issue #27331: The email.mime classes now all accept an optional policy keyword.
+- bpo-27331: The email.mime classes now all accept an optional policy
+  keyword.
 
-- Issue #27988: Fix email iter_attachments incorrect mutation of payload list.
+- bpo-27988: Fix email iter_attachments incorrect mutation of payload list.
 
-- Issue #16113: Add SHA-3 and SHAKE support to hashlib module.
+- bpo-16113: Add SHA-3 and SHAKE support to hashlib module.
 
 - Eliminate a tautological-pointer-compare warning in _scproxy.c.
 
-- Issue #27776: The :func:`os.urandom` function does now block on Linux 3.17
+- bpo-27776: The :func:`os.urandom` function does now block on Linux 3.17
   and newer until the system urandom entropy pool is initialized to increase
   the security. This change is part of the :pep:`524`.
 
-- Issue #27778: Expose the Linux ``getrandom()`` syscall as a new
+- bpo-27778: Expose the Linux ``getrandom()`` syscall as a new
   :func:`os.getrandom` function. This change is part of the :pep:`524`.
 
-- Issue #27691: Fix ssl module's parsing of GEN_RID subject alternative name
+- bpo-27691: Fix ssl module's parsing of GEN_RID subject alternative name
   fields in X.509 certs.
 
-- Issue #18844: Add random.choices().
+- bpo-18844: Add random.choices().
 
-- Issue #25761: Improved error reporting about truncated pickle data in
-  implementation of unpickler.  UnpicklingError is now raised instead of
+- bpo-25761: Improved error reporting about truncated pickle data in C
+  implementation of unpickler.  UnpicklingError is now raised instead of
   AttributeError and ValueError in some cases.
 
-- Issue #26798: Add BLAKE2 (blake2b and blake2s) to hashlib.
+- bpo-26798: Add BLAKE2 (blake2b and blake2s) to hashlib.
 
-- Issue #26032: Optimized globbing in pathlib by using os.scandir(); it is now
+- bpo-26032: Optimized globbing in pathlib by using os.scandir(); it is now
   about 1.5--4 times faster.
 
-- Issue #25596: Optimized glob() and iglob() functions in the
-  glob module; they are now about 3--6 times faster.
+- bpo-25596: Optimized glob() and iglob() functions in the glob module; they
+  are now about 3--6 times faster.
 
-- Issue #27928: Add scrypt (password-based key derivation function) to
-  hashlib module (requires OpenSSL 1.1.0).
+- bpo-27928: Add scrypt (password-based key derivation function) to hashlib
+  module (requires OpenSSL 1.1.0).
 
-- Issue #27850: Remove 3DES from ssl module's default cipher list to counter
+- bpo-27850: Remove 3DES from ssl module's default cipher list to counter
   measure sweet32 attack (CVE-2016-2183).
 
-- Issue #27766: Add ChaCha20 Poly1305 to ssl module's default ciper list.
+- bpo-27766: Add ChaCha20 Poly1305 to ssl module's default ciper list.
   (Required OpenSSL 1.1.0 or LibreSSL).
 
-- Issue #25387: Check return value of winsound.MessageBeep.
+- bpo-25387: Check return value of winsound.MessageBeep.
 
-- Issue #27866: Add SSLContext.get_ciphers() method to get a list of all
+- bpo-27866: Add SSLContext.get_ciphers() method to get a list of all
   enabled ciphers.
 
-- Issue #27744: Add AF_ALG (Linux Kernel crypto) to socket module.
+- bpo-27744: Add AF_ALG (Linux Kernel crypto) to socket module.
 
-- Issue #26470: Port ssl and hashlib module to OpenSSL 1.1.0.
+- bpo-26470: Port ssl and hashlib module to OpenSSL 1.1.0.
 
-- Issue #11620: Fix support for SND_MEMORY in winsound.PlaySound.  Based on a
+- bpo-11620: Fix support for SND_MEMORY in winsound.PlaySound.  Based on a
   patch by Tim Lesher.
 
-- Issue #11734: Add support for IEEE 754 half-precision floats to the
-  struct module. Based on a patch by Eli Stevens.
+- bpo-11734: Add support for IEEE 754 half-precision floats to the struct
+  module. Based on a patch by Eli Stevens.
 
-- Issue #27919: Deprecated ``extra_path`` distribution option in distutils
+- bpo-27919: Deprecated ``extra_path`` distribution option in distutils
   packaging.
 
-- Issue #23229: Add new ``cmath`` constants: ``cmath.inf`` and ``cmath.nan`` to
+- bpo-23229: Add new ``cmath`` constants: ``cmath.inf`` and ``cmath.nan`` to
   match ``math.inf`` and ``math.nan``, and also ``cmath.infj`` and
   ``cmath.nanj`` to match the format used by complex repr.
 
-- Issue #27842: The csv.DictReader now returns rows of type OrderedDict.
+- bpo-27842: The csv.DictReader now returns rows of type OrderedDict.
   (Contributed by Steve Holden.)
 
-- Remove support for passing a file descriptor to os.access. It never worked but
-  previously didn't raise.
+- Remove support for passing a file descriptor to os.access. It never worked
+  but previously didn't raise.
 
-- Issue #12885: Fix error when distutils encounters symlink.
+- bpo-12885: Fix error when distutils encounters symlink.
 
-- Issue #27881: Fixed possible bugs when setting sqlite3.Connection.isolation_level.
-  Based on patch by Xiang Zhang.
+- bpo-27881: Fixed possible bugs when setting
+  sqlite3.Connection.isolation_level. Based on patch by Xiang Zhang.
 
-- Issue #27861: Fixed a crash in sqlite3.Connection.cursor() when a factory
+- bpo-27861: Fixed a crash in sqlite3.Connection.cursor() when a factory
   creates not a cursor.  Patch by Xiang Zhang.
 
-- Issue #19884: Avoid spurious output on OS X with Gnu Readline.
+- bpo-19884: Avoid spurious output on OS X with Gnu Readline.
 
-- Issue #27706: Restore deterministic behavior of random.Random().seed()
-  for string seeds using seeding version 1.  Allows sequences of calls
-  to random() to exactly match those obtained in Python 2.
-  Patch by Nofar Schnider.
+- bpo-27706: Restore deterministic behavior of random.Random().seed() for
+  string seeds using seeding version 1.  Allows sequences of calls to
+  random() to exactly match those obtained in Python 2. Patch by Nofar
+  Schnider.
 
-- Issue #10513: Fix a regression in Connection.commit().  Statements should
-  not be reset after a commit.
+- bpo-10513: Fix a regression in Connection.commit().  Statements should not
+  be reset after a commit.
 
-- Issue #12319: Chunked transfer encoding support added to
+- bpo-12319: Chunked transfer encoding support added to
   http.client.HTTPConnection requests.  The
   urllib.request.AbstractHTTPHandler class does not enforce a Content-Length
   header any more.  If a HTTP request has a file or iterable body, but no
   Content-Length header, the library now falls back to use chunked transfer-
   encoding.
 
-- A new version of typing.py from https://github.com/python/typing:
-  - Collection (only for 3.6) (Issue #27598)
-  - Add FrozenSet to __all__ (upstream #261)
-  - fix crash in _get_type_vars() (upstream #259)
-  - Remove the dict constraint in ForwardRef._eval_type (upstream #252)
+- A new version of typing.py from https://github.com/python/typing: -
+  Collection (only for 3.6) (Issue #27598) - Add FrozenSet to __all__
+  (upstream #261) - fix crash in _get_type_vars() (upstream #259) - Remove
+  the dict constraint in ForwardRef._eval_type (upstream #252)
 
-- Issue #27832: Make ``_normalize`` parameter to ``Fraction`` constuctor
+- bpo-27832: Make ``_normalize`` parameter to ``Fraction`` constuctor
   keyword-only, so that ``Fraction(2, 3, 4)`` now raises ``TypeError``.
 
-- Issue #27539: Fix unnormalised ``Fraction.__pow__`` result in the case
-  of negative exponent and negative base.
+- bpo-27539: Fix unnormalised ``Fraction.__pow__`` result in the case of
+  negative exponent and negative base.
 
-- Issue #21718: cursor.description is now available for queries using CTEs.
+- bpo-21718: cursor.description is now available for queries using CTEs.
 
-- Issue #27819: In distutils sdists, simply produce the "gztar" (gzipped tar
+- bpo-27819: In distutils sdists, simply produce the "gztar" (gzipped tar
   format) distributions on all platforms unless "formats" is supplied.
 
-- Issue #2466: posixpath.ismount now correctly recognizes mount points which
+- bpo-2466: posixpath.ismount now correctly recognizes mount points which
   the user does not have permission to access.
 
-- Issue #9998: On Linux, ctypes.util.find_library now looks in LD_LIBRARY_PATH
+- bpo-9998: On Linux, ctypes.util.find_library now looks in LD_LIBRARY_PATH
   for shared libraries.
 
-- Issue #27573: exit message for code.interact is now configurable.
+- bpo-27573: exit message for code.interact is now configurable.
 
-- Issue #27930: Improved behaviour of logging.handlers.QueueListener.
-  Thanks to Paulo Andrade and Petr Viktorin for the analysis and patch.
+- bpo-27930: Improved behaviour of logging.handlers.QueueListener. Thanks to
+  Paulo Andrade and Petr Viktorin for the analysis and patch.
 
-- Issue #6766: Distributed reference counting added to multiprocessing
-  to support nesting of shared values / proxy objects.
+- bpo-6766: Distributed reference counting added to multiprocessing to
+  support nesting of shared values / proxy objects.
 
-- Issue #21201: Improves readability of multiprocessing error message.  Thanks
+- bpo-21201: Improves readability of multiprocessing error message.  Thanks
   to Wojciech Walczak for patch.
 
 - asyncio: Add set_protocol / get_protocol to Transports.
 
-- Issue #27456: asyncio: Set TCP_NODELAY by default.
+- bpo-27456: asyncio: Set TCP_NODELAY by default.
 
 IDLE
 ----
 
-- Issue #15308: Add 'interrupt execution' (^C) to Shell menu.
-  Patch by Roger Serwy, updated by Bayard Randel.
+- bpo-15308: Add 'interrupt execution' (^C) to Shell menu. Patch by Roger
+  Serwy, updated by Bayard Randel.
 
-- Issue #27922: Stop IDLE tests from 'flashing' gui widgets on the screen.
+- bpo-27922: Stop IDLE tests from 'flashing' gui widgets on the screen.
 
-- Issue #27891: Consistently group and sort imports within idlelib modules.
+- bpo-27891: Consistently group and sort imports within idlelib modules.
 
-- Issue #17642: add larger font sizes for classroom projection.
+- bpo-17642: add larger font sizes for classroom projection.
 
 - Add version to title of IDLE help window.
 
-- Issue #25564: In section on IDLE -- console differences, mention that
-  using exec means that __builtins__ is defined for each statement.
+- bpo-25564: In section on IDLE -- console differences, mention that using
+  exec means that __builtins__ is defined for each statement.
 
-- Issue #27821: Fix 3.6.0a3 regression that prevented custom key sets
-  from being selected when no custom theme was defined.
+- bpo-27821: Fix 3.6.0a3 regression that prevented custom key sets from
+  being selected when no custom theme was defined.
 
 C API
 -----
 
-- Issue #26900: Excluded underscored names and other private API from limited API.
+- bpo-26900: Excluded underscored names and other private API from limited
+  API.
 
-- Issue #26027: Add support for path-like objects in PyUnicode_FSConverter() &
+- bpo-26027: Add support for path-like objects in PyUnicode_FSConverter() &
   PyUnicode_FSDecoder().
 
 Tests
 -----
 
-- Issue #27427: Additional tests for the math module. Patch by Francisco Couzo.
+- bpo-27427: Additional tests for the math module. Patch by Francisco Couzo.
 
-- Issue #27953: Skip math and cmath tests that fail on OS X 10.4 due to a
-  poor libm implementation of tan.
+- bpo-27953: Skip math and cmath tests that fail on OS X 10.4 due to a poor
+  libm implementation of tan.
 
-- Issue #26040: Improve test_math and test_cmath coverage and rigour. Patch by
+- bpo-26040: Improve test_math and test_cmath coverage and rigour. Patch by
   Jeff Allen.
 
-- Issue #27787: Call gc.collect() before checking each test for "dangling
+- bpo-27787: Call gc.collect() before checking each test for "dangling
   threads", since the dangling threads are weak references.
 
 Build
 -----
 
-- Issue #27566: Fix clean target in freeze makefile (patch by Lisa Roach)
+- bpo-27566: Fix clean target in freeze makefile (patch by Lisa Roach)
 
-- Issue #27705: Update message in validate_ucrtbase.py
+- bpo-27705: Update message in validate_ucrtbase.py
 
-- Issue #27976: Deprecate building _ctypes with the bundled copy of libffi on
+- bpo-27976: Deprecate building _ctypes with the bundled copy of libffi on
   non-OSX UNIX platforms.
 
-- Issue #27983: Cause lack of llvm-profdata tool when using clang as
-  required for PGO linking to be a configure time error rather than
-  make time when --with-optimizations is enabled.  Also improve our
-  ability to find the llvm-profdata tool on MacOS and some Linuxes.
+- bpo-27983: Cause lack of llvm-profdata tool when using clang as required
+  for PGO linking to be a configure time error rather than make time when
+  --with- optimizations is enabled.  Also improve our ability to find the
+  llvm- profdata tool on MacOS and some Linuxes.
 
-- Issue #21590: Support for DTrace and SystemTap probes.
+- bpo-21590: Support for DTrace and SystemTap probes.
 
-- Issue #26307: The profile-opt build now applies PGO to the built-in modules.
+- bpo-26307: The profile-opt build now applies PGO to the built-in modules.
 
-- Issue #26359: Add the --with-optimizations flag to turn on LTO and PGO build
+- bpo-26359: Add the --with-optimizations flag to turn on LTO and PGO build
   support when available.
 
-- Issue #27917: Set platform triplets for Android builds.
+- bpo-27917: Set platform triplets for Android builds.
 
-- Issue #25825: Update references to the $(LIBPL) installation path on AIX.
+- bpo-25825: Update references to the $(LIBPL) installation path on AIX.
   This path was changed in 3.2a4.
 
 - Update OS X installer to use SQLite 3.14.1 and XZ 5.2.2.
 
-- Issue #21122: Fix LTO builds on OS X.
+- bpo-21122: Fix LTO builds on OS X.
 
-- Issue #17128: Build OS X installer with a private copy of OpenSSL.
-  Also provide a sample Install Certificates command script to install a
-  set of root certificates from the third-party certifi module.
+- bpo-17128: Build OS X installer with a private copy of OpenSSL. Also
+  provide a sample Install Certificates command script to install a set of
+  root certificates from the third-party certifi module.
 
 Tools/Demos
 -----------
 
-- Issue #27952: Get Tools/scripts/fixcid.py working with Python 3 and the
+- bpo-27952: Get Tools/scripts/fixcid.py working with Python 3 and the
   current "re" module, avoid invalid Python backslash escapes, and fix a bug
   parsing escaped C quote signs.
 
 Windows
 -------
 
-- Issue #28065: Update xz dependency to 5.2.2 and build it from source.
+- bpo-28065: Update xz dependency to 5.2.2 and build it from source.
 
-- Issue #25144: Ensures TargetDir is set before continuing with custom
-  install.
+- bpo-25144: Ensures TargetDir is set before continuing with custom install.
 
-- Issue #1602: Windows console doesn't input or print Unicode (PEP 528)
+- bpo-1602: Windows console doesn't input or print Unicode (PEP 528)
 
-- Issue #27781: Change file system encoding on Windows to UTF-8 (PEP 529)
+- bpo-27781: Change file system encoding on Windows to UTF-8 (PEP 529)
 
-- Issue #27731: Opt-out of MAX_PATH on Windows 10
+- bpo-27731: Opt-out of MAX_PATH on Windows 10
 
-- Issue #6135: Adds encoding and errors parameters to subprocess.
+- bpo-6135: Adds encoding and errors parameters to subprocess.
 
-- Issue #27959: Adds oem encoding, alias ansi to mbcs, move aliasmbcs to
-  codec lookup.
+- bpo-27959: Adds oem encoding, alias ansi to mbcs, move aliasmbcs to codec
+  lookup.
 
-- Issue #27982: The functions of the winsound module now accept keyword
+- bpo-27982: The functions of the winsound module now accept keyword
   arguments.
 
-- Issue #20366: Build full text search support into SQLite on Windows.
+- bpo-20366: Build full text search support into SQLite on Windows.
 
-- Issue #27756: Adds new icons for Python files and processes on Windows.
+- bpo-27756: Adds new icons for Python files and processes on Windows.
   Designs by Cherry Wang.
 
-- Issue #27883: Update sqlite to 3.14.1.0 on Windows.
+- bpo-27883: Update sqlite to 3.14.1.0 on Windows.
 
 
 What's New in Python 3.6.0 alpha 4?
@@ -1847,233 +2351,233 @@ What's New in Python 3.6.0 alpha 4?
 Core and Builtins
 -----------------
 
-- Issue #27704: Optimized creating bytes and bytearray from byte-like objects
-  and iterables.  Speed up to 3 times for short objects.  Original patch by
+- bpo-27704: Optimized creating bytes and bytearray from byte-like objects
+  and iterables. Speed up to 3 times for short objects.  Original patch by
   Naoki Inada.
 
-- Issue #26823: Large sections of repeated lines in tracebacks are now
-  abbreviated as "[Previous line repeated {count} more times]" by the builtin
-  traceback rendering. Patch by Emanuel Barry.
+- bpo-26823: Large sections of repeated lines in tracebacks are now
+  abbreviated as "[Previous line repeated {count} more times]" by the
+  builtin traceback rendering. Patch by Emanuel Barry.
 
-- Issue #27574: Decreased an overhead of parsing keyword arguments in functions
+- bpo-27574: Decreased an overhead of parsing keyword arguments in functions
   implemented with using Argument Clinic.
 
-- Issue #22557: Now importing already imported modules is up to 2.5 times
+- bpo-22557: Now importing already imported modules is up to 2.5 times
   faster.
 
-- Issue #17596: Include <wincrypt.h> to help with Min GW building.
+- bpo-17596: Include <wincrypt.h> to help with Min GW building.
 
-- Issue #17599: On Windows, rename the privately defined REPARSE_DATA_BUFFER
+- bpo-17599: On Windows, rename the privately defined REPARSE_DATA_BUFFER
   structure to avoid conflicting with the definition from Min GW.
 
-- Issue #27507: Add integer overflow check in bytearray.extend().  Patch by
+- bpo-27507: Add integer overflow check in bytearray.extend().  Patch by
   Xiang Zhang.
 
-- Issue #27581: Don't rely on wrapping for overflow check in
+- bpo-27581: Don't rely on wrapping for overflow check in
   PySequence_Tuple().  Patch by Xiang Zhang.
 
-- Issue #1621: Avoid signed integer overflow in list and tuple operations.
+- bpo-1621: Avoid signed integer overflow in list and tuple operations.
   Patch by Xiang Zhang.
 
-- Issue #27419: Standard __import__() no longer look up "__import__" in globals
+- bpo-27419: Standard __import__() no longer look up "__import__" in globals
   or builtins for importing submodules or "from import".  Fixed a crash if
   raise a warning about unabling to resolve package from __spec__ or
   __package__.
 
-- Issue #27083: Respect the PYTHONCASEOK environment variable under Windows.
+- bpo-27083: Respect the PYTHONCASEOK environment variable under Windows.
 
-- Issue #27514: Make having too many statically nested blocks a SyntaxError
+- bpo-27514: Make having too many statically nested blocks a SyntaxError
   instead of SystemError.
 
-- Issue #27366: Implemented PEP 487 (Simpler customization of class creation).
+- bpo-27366: Implemented PEP 487 (Simpler customization of class creation).
   Upon subclassing, the __init_subclass__ classmethod is called on the base
   class. Descriptors are initialized with __set_name__ after class creation.
 
 Library
 -------
 
-- Issue #26027, #27524: Add PEP 519/__fspath__() support to the os and os.path
-  modules. Includes code from Jelle Zijlstra.
+- bpo-26027: Add PEP 519/__fspath__() support to the os and os.path modules.
+  Includes code from Jelle Zijlstra. (See also: bpo-27524)
 
-- Issue #27598: Add Collections to collections.abc.
-  Patch by Ivan Levkivskyi, docs by Neil Girdhar.
+- bpo-27598: Add Collections to collections.abc. Patch by Ivan Levkivskyi,
+  docs by Neil Girdhar.
 
-- Issue #25958: Support "anti-registration" of special methods from
-  various ABCs, like __hash__, __iter__ or __len__.  All these (and
-  several more) can be set to None in an implementation class and the
-  behavior will be as if the method is not defined at all.
-  (Previously, this mechanism existed only for __hash__, to make
-  mutable classes unhashable.)  Code contributed by Andrew Barnert and
-  Ivan Levkivskyi.
+- bpo-25958: Support "anti-registration" of special methods from various
+  ABCs, like __hash__, __iter__ or __len__.  All these (and several more)
+  can be set to None in an implementation class and the behavior will be as
+  if the method is not defined at all. (Previously, this mechanism existed
+  only for __hash__, to make mutable classes unhashable.)  Code contributed
+  by Andrew Barnert and Ivan Levkivskyi.
 
-- Issue #16764: Support keyword arguments to zlib.decompress().  Patch by
-  Xiang Zhang.
+- bpo-16764: Support keyword arguments to zlib.decompress().  Patch by Xiang
+  Zhang.
 
-- Issue #27736: Prevent segfault after interpreter re-initialization due
-  to ref count problem introduced in code for Issue #27038 in 3.6.0a3.
-  Patch by Xiang Zhang.
+- bpo-27736: Prevent segfault after interpreter re-initialization due to ref
+  count problem introduced in code for Issue #27038 in 3.6.0a3. Patch by
+  Xiang Zhang.
 
-- Issue #25628:  The *verbose* and *rename* parameters for
+- bpo-25628: The *verbose* and *rename* parameters for
   collections.namedtuple are now keyword-only.
 
-- Issue #12345: Add mathematical constant tau to math and cmath. See also
-  PEP 628.
+- bpo-12345: Add mathematical constant tau to math and cmath. See also PEP
+  628.
 
-- Issue #26823: traceback.StackSummary.format now abbreviates large sections of
-  repeated lines as "[Previous line repeated {count} more times]" (this change
-  then further affects other traceback display operations in the module). Patch
-  by Emanuel Barry.
+- bpo-26823: traceback.StackSummary.format now abbreviates large sections of
+  repeated lines as "[Previous line repeated {count} more times]" (this
+  change then further affects other traceback display operations in the
+  module). Patch by Emanuel Barry.
 
-- Issue #27664: Add to concurrent.futures.thread.ThreadPoolExecutor()
-  the ability to specify a thread name prefix.
+- bpo-27664: Add to concurrent.futures.thread.ThreadPoolExecutor() the
+  ability to specify a thread name prefix.
 
-- Issue #27181: Add geometric_mean and harmonic_mean to statistics module.
+- bpo-27181: Add geometric_mean and harmonic_mean to statistics module.
 
-- Issue #27573: code.interact now prints an message when exiting.
+- bpo-27573: code.interact now prints an message when exiting.
 
-- Issue #6422: Add autorange method to timeit.Timer objects.
+- bpo-6422: Add autorange method to timeit.Timer objects.
 
-- Issue #27773: Correct some memory management errors server_hostname in
+- bpo-27773: Correct some memory management errors server_hostname in
   _ssl.wrap_socket().
 
-- Issue #26750: unittest.mock.create_autospec() now works properly for
+- bpo-26750: unittest.mock.create_autospec() now works properly for
   subclasses of property() and other data descriptors.  Removes the never
   publicly used, never documented unittest.mock.DescriptorTypes tuple.
 
-- Issue #26754: Undocumented support of general bytes-like objects
-  as path in compile() and similar functions is now deprecated.
+- bpo-26754: Undocumented support of general bytes-like objects as path in
+  compile() and similar functions is now deprecated.
 
-- Issue #26800: Undocumented support of general bytes-like objects
-  as paths in os functions is now deprecated.
+- bpo-26800: Undocumented support of general bytes-like objects as paths in
+  os functions is now deprecated.
 
-- Issue #26981: Add _order_ compatibility shim to enum.Enum for
-  Python 2/3 code bases.
+- bpo-26981: Add _order_ compatibility shim to enum.Enum for Python 2/3 code
+  bases.
 
-- Issue #27661: Added tzinfo keyword argument to datetime.combine.
+- bpo-27661: Added tzinfo keyword argument to datetime.combine.
 
-- In the curses module, raise an error if window.getstr() or window.instr() is
-  passed a negative value.
+- In the curses module, raise an error if window.getstr() or window.instr()
+  is passed a negative value.
 
-- Issue #27783: Fix possible usage of uninitialized memory in
+- bpo-27783: Fix possible usage of uninitialized memory in
   operator.methodcaller.
 
-- Issue #27774: Fix possible Py_DECREF on unowned object in _sre.
+- bpo-27774: Fix possible Py_DECREF on unowned object in _sre.
 
-- Issue #27760: Fix possible integer overflow in binascii.b2a_qp.
+- bpo-27760: Fix possible integer overflow in binascii.b2a_qp.
 
-- Issue #27758: Fix possible integer overflow in the _csv module for large
+- bpo-27758: Fix possible integer overflow in the _csv module for large
   record lengths.
 
-- Issue #27568: Prevent HTTPoxy attack (CVE-2016-1000110). Ignore the
-  HTTP_PROXY variable when REQUEST_METHOD environment is set, which indicates
-  that the script is in CGI mode.
+- bpo-27568: Prevent HTTPoxy attack (CVE-2016-1000110). Ignore the
+  HTTP_PROXY variable when REQUEST_METHOD environment is set, which
+  indicates that the script is in CGI mode.
 
-- Issue #7063: Remove dead code from the "array" module's slice handling.
-  Patch by Chuck.
+- bpo-7063: Remove dead code from the "array" module's slice handling. Patch
+  by Chuck.
 
-- Issue #27656: Do not assume sched.h defines any SCHED_* constants.
+- bpo-27656: Do not assume sched.h defines any SCHED_* constants.
 
-- Issue #27130: In the "zlib" module, fix handling of large buffers
-  (typically 4 GiB) when compressing and decompressing.  Previously, inputs
-  were limited to 4 GiB, and compression and decompression operations did not
+- bpo-27130: In the "zlib" module, fix handling of large buffers (typically
+  4 GiB) when compressing and decompressing.  Previously, inputs were
+  limited to 4 GiB, and compression and decompression operations did not
   properly handle results of 4 GiB.
 
-- Issue #24773: Implemented PEP 495 (Local Time Disambiguation).
+- bpo-24773: Implemented PEP 495 (Local Time Disambiguation).
 
-- Expose the EPOLLEXCLUSIVE constant (when it is defined) in the select module.
+- Expose the EPOLLEXCLUSIVE constant (when it is defined) in the select
+  module.
 
-- Issue #27567: Expose the EPOLLRDHUP and POLLRDHUP constants in the select
+- bpo-27567: Expose the EPOLLRDHUP and POLLRDHUP constants in the select
   module.
 
-- Issue #1621: Avoid signed int negation overflow in the "audioop" module.
+- bpo-1621: Avoid signed int negation overflow in the "audioop" module.
 
-- Issue #27533: Release GIL in nt._isdir
+- bpo-27533: Release GIL in nt._isdir
 
-- Issue #17711: Fixed unpickling by the persistent ID with protocol 0.
-  Original patch by Alexandre Vassalotti.
+- bpo-17711: Fixed unpickling by the persistent ID with protocol 0. Original
+  patch by Alexandre Vassalotti.
 
-- Issue #27522: Avoid an unintentional reference cycle in email.feedparser.
+- bpo-27522: Avoid an unintentional reference cycle in email.feedparser.
 
-- Issue #27512: Fix a segfault when os.fspath() called an __fspath__() method
+- bpo-27512: Fix a segfault when os.fspath() called an __fspath__() method
   that raised an exception. Patch by Xiang Zhang.
 
 IDLE
 ----
 
-- Issue #27714: text_textview and test_autocomplete now pass when re-run
-  in the same process.  This occurs when test_idle fails when run with the
-  -w option but without -jn.  Fix warning from test_config.
+- bpo-27714: text_textview and test_autocomplete now pass when re-run in the
+  same process.  This occurs when test_idle fails when run with the -w
+  option but without -jn.  Fix warning from test_config.
 
-- Issue #27621: Put query response validation error messages in the query
-  box itself instead of in a separate massagebox.  Redo tests to match.
-  Add Mac OSX refinements.  Original patch by Mark Roseman.
+- bpo-27621: Put query response validation error messages in the query box
+  itself instead of in a separate massagebox.  Redo tests to match. Add Mac
+  OSX refinements. Original patch by Mark Roseman.
 
-- Issue #27620: Escape key now closes Query box as cancelled.
+- bpo-27620: Escape key now closes Query box as cancelled.
 
-- Issue #27609: IDLE: tab after initial whitespace should tab, not
-  autocomplete. This fixes problem with writing docstrings at least
-  twice indented.
+- bpo-27609: IDLE: tab after initial whitespace should tab, not
+  autocomplete. This fixes problem with writing docstrings at least twice
+  indented.
 
-- Issue #27609: Explicitly return None when there are also non-None
-  returns. In a few cases, reverse a condition and eliminate a return.
+- bpo-27609: Explicitly return None when there are also non-None returns. In
+  a few cases, reverse a condition and eliminate a return.
 
-- Issue #25507: IDLE no longer runs buggy code because of its tkinter imports.
+- bpo-25507: IDLE no longer runs buggy code because of its tkinter imports.
   Users must include the same imports required to run directly in Python.
 
-- Issue #27173: Add 'IDLE Modern Unix' to the built-in key sets.
-  Make the default key set depend on the platform.
-  Add tests for the changes to the config module.
+- bpo-27173: Add 'IDLE Modern Unix' to the built-in key sets. Make the
+  default key set depend on the platform. Add tests for the changes to the
+  config module.
 
-- Issue #27452: add line counter and crc to IDLE configHandler test dump.
+- bpo-27452: add line counter and crc to IDLE configHandler test dump.
 
 Tests
 -----
 
-- Issue #25805: Skip a test in test_pkgutil as needed that doesn't work when
+- bpo-25805: Skip a test in test_pkgutil as needed that doesn't work when
   ``__name__ == __main__``. Patch by SilentGhost.
 
-- Issue #27472: Add test.support.unix_shell as the path to the default shell.
+- bpo-27472: Add test.support.unix_shell as the path to the default shell.
 
-- Issue #27369: In test_pyexpat, avoid testing an error message detail that
+- bpo-27369: In test_pyexpat, avoid testing an error message detail that
   changed in Expat 2.2.0.
 
-- Issue #27594: Prevent assertion error when running test_ast with coverage
-  enabled: ensure code object has a valid first line number.
-  Patch suggested by Ivan Levkivskyi.
+- bpo-27594: Prevent assertion error when running test_ast with coverage
+  enabled: ensure code object has a valid first line number. Patch suggested
+  by Ivan Levkivskyi.
 
 Windows
 -------
 
-- Issue #27647: Update bundled Tcl/Tk to 8.6.6.
+- bpo-27647: Update bundled Tcl/Tk to 8.6.6.
 
-- Issue #27610: Adds PEP 514 metadata to Windows installer
+- bpo-27610: Adds PEP 514 metadata to Windows installer
 
-- Issue #27469: Adds a shell extension to the launcher so that drag and drop
+- bpo-27469: Adds a shell extension to the launcher so that drag and drop
   works correctly.
 
-- Issue #27309: Enables proper Windows styles in python[w].exe manifest.
+- bpo-27309: Enables proper Windows styles in python[w].exe manifest.
 
 Build
 -----
 
-- Issue #27713: Suppress spurious build warnings when updating importlib's
-  bootstrap files.  Patch by Xiang Zhang
+- bpo-27713: Suppress spurious build warnings when updating importlib's
+  bootstrap files. Patch by Xiang Zhang
 
-- Issue #25825: Correct the references to Modules/python.exp, which is
-  required on AIX.  The references were accidentally changed in 3.5.0a1.
+- bpo-25825: Correct the references to Modules/python.exp, which is required
+  on AIX.  The references were accidentally changed in 3.5.0a1.
 
-- Issue #27453: CPP invocation in configure must use CPPFLAGS. Patch by
-  Chi Hsuan Yen.
+- bpo-27453: CPP invocation in configure must use CPPFLAGS. Patch by Chi
+  Hsuan Yen.
 
-- Issue #27641: The configure script now inserts comments into the makefile
-  to prevent the pgen and _freeze_importlib executables from being cross-
+- bpo-27641: The configure script now inserts comments into the makefile to
+  prevent the pgen and _freeze_importlib executables from being cross-
   compiled.
 
-- Issue #26662: Set PYTHON_FOR_GEN in configure as the Python program to be
+- bpo-26662: Set PYTHON_FOR_GEN in configure as the Python program to be
   used for file generation during the build.
 
-- Issue #10910: Avoid C++ compilation errors on FreeBSD and OS X.
-  Also update FreedBSD version checks for the original ctype UTF-8 workaround.
+- bpo-10910: Avoid C++ compilation errors on FreeBSD and OS X. Also update
+  FreedBSD version checks for the original ctype UTF-8 workaround.
 
 
 What's New in Python 3.6.0 alpha 3?
@@ -2084,203 +2588,215 @@ What's New in Python 3.6.0 alpha 3?
 Core and Builtins
 -----------------
 
-- Issue #27473: Fixed possible integer overflow in bytes and bytearray
-  concatenations.  Patch by Xiang Zhang.
+- bpo-27473: Fixed possible integer overflow in bytes and bytearray
+  concatenations. Patch by Xiang Zhang.
 
-- Issue #23034: The output of a special Python build with defined COUNT_ALLOCS,
-  SHOW_ALLOC_COUNT or SHOW_TRACK_COUNT macros is now off by  default.  It can
-  be re-enabled using the "-X showalloccount" option.  It now outputs to stderr
-  instead of stdout.
+- bpo-23034: The output of a special Python build with defined COUNT_ALLOCS,
+  SHOW_ALLOC_COUNT or SHOW_TRACK_COUNT macros is now off by  default.  It
+  can be re-enabled using the "-X showalloccount" option.  It now outputs to
+  stderr instead of stdout.
 
-- Issue #27443: __length_hint__() of bytearray iterators no longer return a
+- bpo-27443: __length_hint__() of bytearray iterators no longer return a
   negative integer for a resized bytearray.
 
-- Issue #27007: The fromhex() class methods of bytes and bytearray subclasses
+- bpo-27007: The fromhex() class methods of bytes and bytearray subclasses
   now return an instance of corresponding subclass.
 
 Library
 -------
 
-- Issue #26844: Fix error message for imp.find_module() to refer to 'path'
+- bpo-26844: Fix error message for imp.find_module() to refer to 'path'
   instead of 'name'. Patch by Lev Maximov.
 
-- Issue #23804: Fix SSL zero-length recv() calls to not block and not raise
-  an error about unclean EOF.
+- bpo-23804: Fix SSL zero-length recv() calls to not block and not raise an
+  error about unclean EOF.
 
-- Issue #27466: Change time format returned by http.cookie.time2netscape,
+- bpo-27466: Change time format returned by http.cookie.time2netscape,
   confirming the netscape cookie format and making it consistent with
   documentation.
 
-- Issue #21708: Deprecated dbm.dumb behavior that differs from common dbm
-  behavior: creating a database in 'r' and 'w' modes and modifying a database
-  in 'r' mode.
+- bpo-21708: Deprecated dbm.dumb behavior that differs from common dbm
+  behavior: creating a database in 'r' and 'w' modes and modifying a
+  database in 'r' mode.
 
-- Issue #26721: Change the socketserver.StreamRequestHandler.wfile attribute
-  to implement BufferedIOBase. In particular, the write() method no longer
-  does partial writes.
+- bpo-26721: Change the socketserver.StreamRequestHandler.wfile attribute to
+  implement BufferedIOBase. In particular, the write() method no longer does
+  partial writes.
 
-- Issue #22115: Added methods trace_add, trace_remove and trace_info in the
+- bpo-22115: Added methods trace_add, trace_remove and trace_info in the
   tkinter.Variable class.  They replace old methods trace_variable, trace,
-  trace_vdelete and trace_vinfo that use obsolete Tcl commands and might
-  not work in future versions of Tcl.  Fixed old tracing methods:
+  trace_vdelete and trace_vinfo that use obsolete Tcl commands and might not
+  work in future versions of Tcl.  Fixed old tracing methods:
   trace_vdelete() with wrong mode no longer break tracing, trace_vinfo() now
-  always returns a list of pairs of strings, tracing in the "u" mode now works.
+  always returns a list of pairs of strings, tracing in the "u" mode now
+  works.
 
-- Issue #26243: Only the level argument to zlib.compress() is keyword argument
+- bpo-26243: Only the level argument to zlib.compress() is keyword argument
   now.  The first argument is positional-only.
 
-- Issue #27038: Expose the DirEntry type as os.DirEntry. Code patch by
-  Jelle Zijlstra.
+- bpo-27038: Expose the DirEntry type as os.DirEntry. Code patch by Jelle
+  Zijlstra.
 
-- Issue #27186: Update os.fspath()/PyOS_FSPath() to check the return value of
+- bpo-27186: Update os.fspath()/PyOS_FSPath() to check the return value of
   __fspath__() to be either str or bytes.
 
-- Issue #18726: All optional parameters of the dump(), dumps(),
-  load() and loads() functions and JSONEncoder and JSONDecoder class
-  constructors in the json module are now keyword-only.
+- bpo-18726: All optional parameters of the dump(), dumps(), load() and
+  loads() functions and JSONEncoder and JSONDecoder class constructors in
+  the json module are now keyword-only.
 
-- Issue #27319: Methods selection_set(), selection_add(), selection_remove()
+- bpo-27319: Methods selection_set(), selection_add(), selection_remove()
   and selection_toggle() of ttk.TreeView now allow passing multiple items as
   multiple arguments instead of passing them as a tuple.  Deprecated
   undocumented ability of calling the selection() method with arguments.
 
-- Issue #27079: Fixed curses.ascii functions isblank(), iscntrl() and ispunct().
+- bpo-27079: Fixed curses.ascii functions isblank(), iscntrl() and
+  ispunct().
 
-- Issue #27294: Numerical state in the repr for Tkinter event objects is now
+- bpo-27294: Numerical state in the repr for Tkinter event objects is now
   represented as a combination of known flags.
 
-- Issue #27177: Match objects in the re module now support index-like objects
+- bpo-27177: Match objects in the re module now support index-like objects
   as group indices.  Based on patches by Jeroen Demeyer and Xiang Zhang.
 
-- Issue #26754: Some functions (compile() etc) accepted a filename argument
+- bpo-26754: Some functions (compile() etc) accepted a filename argument
   encoded as an iterable of integers. Now only strings and byte-like objects
   are accepted.
 
-- Issue #26536: socket.ioctl now supports SIO_LOOPBACK_FAST_PATH. Patch by
+- bpo-26536: socket.ioctl now supports SIO_LOOPBACK_FAST_PATH. Patch by
   Daniel Stokes.
 
-- Issue #27048: Prevents distutils failing on Windows when environment
+- bpo-27048: Prevents distutils failing on Windows when environment
   variables contain non-ASCII characters
 
-- Issue #27330: Fixed possible leaks in the ctypes module.
+- bpo-27330: Fixed possible leaks in the ctypes module.
 
-- Issue #27238: Got rid of bare excepts in the turtle module.  Original patch
+- bpo-27238: Got rid of bare excepts in the turtle module.  Original patch
   by Jelle Zijlstra.
 
-- Issue #27122: When an exception is raised within the context being managed
-  by a contextlib.ExitStack() and one of the exit stack generators
-  catches and raises it in a chain, do not re-raise the original exception
-  when exiting, let the new chained one through.  This avoids the PEP 479
-  bug described in issue25782.
+- bpo-27122: When an exception is raised within the context being managed by
+  a contextlib.ExitStack() and one of the exit stack generators catches and
+  raises it in a chain, do not re-raise the original exception when exiting,
+  let the new chained one through.  This avoids the PEP 479 bug described in
+  issue25782.
 
-- [Security] Issue #27278: Fix os.urandom() implementation using getrandom() on
-  Linux.  Truncate size to INT_MAX and loop until we collected enough random
-  bytes, instead of casting a directly Py_ssize_t to int.
+Security
+--------
 
-- Issue #16864: sqlite3.Cursor.lastrowid now supports REPLACE statement.
+- bpo-27278: Fix os.urandom() implementation using getrandom() on Linux.
+  Truncate size to INT_MAX and loop until we collected enough random bytes,
+  instead of casting a directly Py_ssize_t to int.
+
+Library
+-------
+
+- bpo-16864: sqlite3.Cursor.lastrowid now supports REPLACE statement.
   Initial patch by Alex LordThorsen.
 
-- Issue #26386: Fixed ttk.TreeView selection operations with item id's
+- bpo-26386: Fixed ttk.TreeView selection operations with item id's
   containing spaces.
 
-- Issue #8637: Honor a pager set by the env var MANPAGER (in preference to
-  one set by the env var PAGER).
+- bpo-8637: Honor a pager set by the env var MANPAGER (in preference to one
+  set by the env var PAGER).
 
-- [Security] Issue #22636: Avoid shell injection problems with
-  ctypes.util.find_library().
+Security
+--------
 
-- Issue #16182: Fix various functions in the "readline" module to use the
-  locale encoding, and fix get_begidx() and get_endidx() to return code point
-  indexes.
+- bpo-22636: Avoid shell injection problems with ctypes.util.find_library().
 
-- Issue #27392: Add loop.connect_accepted_socket().
-  Patch by Jim Fulton.
+Library
+-------
+
+- bpo-16182: Fix various functions in the "readline" module to use the
+  locale encoding, and fix get_begidx() and get_endidx() to return code
+  point indexes.
+
+- bpo-27392: Add loop.connect_accepted_socket(). Patch by Jim Fulton.
 
 IDLE
 ----
 
-- Issue #27477: IDLE search dialogs now use ttk widgets.
+- bpo-27477: IDLE search dialogs now use ttk widgets.
 
-- Issue #27173: Add 'IDLE Modern Unix' to the built-in key sets.
-  Make the default key set depend on the platform.
-  Add tests for the changes to the config module.
+- bpo-27173: Add 'IDLE Modern Unix' to the built-in key sets. Make the
+  default key set depend on the platform. Add tests for the changes to the
+  config module.
 
-- Issue #27452: make command line "idle-test> python test_help.py" work.
+- bpo-27452: make command line "idle-test> python test_help.py" work.
   __file__ is relative when python is started in the file's directory.
 
-- Issue #27452: add line counter and crc to IDLE configHandler test dump.
+- bpo-27452: add line counter and crc to IDLE configHandler test dump.
 
-- Issue #27380: IDLE: add query.py with base Query dialog and ttk widgets.
+- bpo-27380: IDLE: add query.py with base Query dialog and ttk widgets.
   Module had subclasses SectionName, ModuleName, and HelpSource, which are
   used to get information from users by configdialog and file =>Load Module.
   Each subclass has itw own validity checks.  Using ModuleName allows users
-  to edit bad module names instead of starting over.
-  Add tests and delete the two files combined into the new one.
+  to edit bad module names instead of starting over. Add tests and delete
+  the two files combined into the new one.
 
-- Issue #27372: Test_idle no longer changes the locale.
+- bpo-27372: Test_idle no longer changes the locale.
 
-- Issue #27365: Allow non-ascii chars in IDLE NEWS.txt, for contributor names.
+- bpo-27365: Allow non-ascii chars in IDLE NEWS.txt, for contributor names.
 
-- Issue #27245: IDLE: Cleanly delete custom themes and key bindings.
+- bpo-27245: IDLE: Cleanly delete custom themes and key bindings.
   Previously, when IDLE was started from a console or by import, a cascade
-  of warnings was emitted.  Patch by Serhiy Storchaka.
+  of warnings was emitted. Patch by Serhiy Storchaka.
 
-- Issue #24137: Run IDLE, test_idle, and htest with tkinter default root
-  disabled.  Fix code and tests that fail with this restriction.  Fix htests to
-  not create a second and redundant root and mainloop.
+- bpo-24137: Run IDLE, test_idle, and htest with tkinter default root
+  disabled.  Fix code and tests that fail with this restriction.  Fix htests
+  to not create a second and redundant root and mainloop.
 
-- Issue #27310: Fix IDLE.app failure to launch on OS X due to vestigial import.
+- bpo-27310: Fix IDLE.app failure to launch on OS X due to vestigial import.
 
 C API
 -----
 
-- Issue #26754: PyUnicode_FSDecoder() accepted a filename argument encoded as
-  an iterable of integers. Now only strings and byte-like objects are accepted.
+- bpo-26754: PyUnicode_FSDecoder() accepted a filename argument encoded as
+  an iterable of integers. Now only strings and byte-like objects are
+  accepted.
 
 Build
 -----
 
-- Issue #28066: Fix the logic that searches build directories for generated
+- bpo-28066: Fix the logic that searches build directories for generated
   include files when building outside the source tree.
 
-- Issue #27442: Expose the Android API level that python was built against, in
+- bpo-27442: Expose the Android API level that python was built against, in
   sysconfig.get_config_vars() as 'ANDROID_API_LEVEL'.
 
-- Issue #27434: The interpreter that runs the cross-build, found in PATH, must
+- bpo-27434: The interpreter that runs the cross-build, found in PATH, must
   now be of the same feature version (e.g. 3.6) as the source being built.
 
-- Issue #26930: Update Windows builds to use OpenSSL 1.0.2h.
+- bpo-26930: Update Windows builds to use OpenSSL 1.0.2h.
 
-- Issue #23968: Rename the platform directory from plat-$(MACHDEP) to
-  plat-$(PLATFORM_TRIPLET).
-  Rename the config directory (LIBPL) from config-$(LDVERSION) to
-  config-$(LDVERSION)-$(PLATFORM_TRIPLET).
-  Install the platform specifc _sysconfigdata module into the platform
-  directory and rename it to include the ABIFLAGS.
+- bpo-23968: Rename the platform directory from plat-$(MACHDEP) to
+  plat-$(PLATFORM_TRIPLET). Rename the config directory (LIBPL) from
+  config-$(LDVERSION) to config-$(LDVERSION)-$(PLATFORM_TRIPLET). Install
+  the platform specifc _sysconfigdata module into the platform directory and
+  rename it to include the ABIFLAGS.
 
 - Don't use largefile support for GNU/Hurd.
 
 Tools/Demos
 -----------
 
-- Issue #27332: Fixed the type of the first argument of module-level functions
+- bpo-27332: Fixed the type of the first argument of module-level functions
   generated by Argument Clinic.  Patch by Petr Viktorin.
 
-- Issue #27418: Fixed Tools/importbench/importbench.py.
+- bpo-27418: Fixed Tools/importbench/importbench.py.
 
 Documentation
 -------------
 
-- Issue #19489: Moved the search box from the sidebar to the header and footer
-  of each page.  Patch by Ammar Askar.
+- bpo-19489: Moved the search box from the sidebar to the header and footer
+  of each page. Patch by Ammar Askar.
 
-- Issue #27285: Update documentation to reflect the deprecation of ``pyvenv``
+- bpo-27285: Update documentation to reflect the deprecation of ``pyvenv``
   and normalize on the term "virtual environment". Patch by Steve Piercy.
 
 Tests
 -----
 
-- Issue #27027: Added test.support.is_android that is True when this is an
+- bpo-27027: Added test.support.is_android that is True when this is an
   Android build.
 
 
@@ -2292,43 +2808,43 @@ What's New in Python 3.6.0 alpha 2?
 Core and Builtins
 -----------------
 
-- Issue #27095: Simplified MAKE_FUNCTION and removed MAKE_CLOSURE opcodes.
+- bpo-27095: Simplified MAKE_FUNCTION and removed MAKE_CLOSURE opcodes.
   Patch by Demur Rumed.
 
-- Issue #27190: Raise NotSupportedError if sqlite3 is older than 3.3.1.
-  Patch by Dave Sawyer.
+- bpo-27190: Raise NotSupportedError if sqlite3 is older than 3.3.1. Patch
+  by Dave Sawyer.
 
-- Issue #27286: Fixed compiling BUILD_MAP_UNPACK_WITH_CALL opcode.  Calling
-  function with generalized unpacking (PEP 448) and conflicting keyword names
-  could cause undefined behavior.
+- bpo-27286: Fixed compiling BUILD_MAP_UNPACK_WITH_CALL opcode.  Calling
+  function with generalized unpacking (PEP 448) and conflicting keyword
+  names could cause undefined behavior.
 
-- Issue #27140: Added BUILD_CONST_KEY_MAP opcode.
+- bpo-27140: Added BUILD_CONST_KEY_MAP opcode.
 
-- Issue #27186: Add support for os.PathLike objects to open() (part of PEP 519).
+- bpo-27186: Add support for os.PathLike objects to open() (part of PEP
+  519).
 
-- Issue #27066: Fixed SystemError if a custom opener (for open()) returns a
+- bpo-27066: Fixed SystemError if a custom opener (for open()) returns a
   negative number without setting an exception.
 
-- Issue #26983: float() now always return an instance of exact float.
-  The deprecation warning is emitted if __float__ returns an instance of
-  a strict subclass of float.  In a future versions of Python this can
-  be an error.
+- bpo-26983: float() now always return an instance of exact float. The
+  deprecation warning is emitted if __float__ returns an instance of a
+  strict subclass of float.  In a future versions of Python this can be an
+  error.
 
-- Issue #27097: Python interpreter is now about 7% faster due to optimized
+- bpo-27097: Python interpreter is now about 7% faster due to optimized
   instruction decoding.  Based on patch by Demur Rumed.
 
-- Issue #26647: Python interpreter now uses 16-bit wordcode instead of bytecode.
-  Patch by Demur Rumed.
+- bpo-26647: Python interpreter now uses 16-bit wordcode instead of
+  bytecode. Patch by Demur Rumed.
 
-- Issue #23275: Allow assigning to an empty target list in round brackets:
-  () = iterable.
+- bpo-23275: Allow assigning to an empty target list in round brackets: () =
+  iterable.
 
-- Issue #27243: Update the __aiter__ protocol: instead of returning
-  an awaitable that resolves to an asynchronous iterator, the asynchronous
+- bpo-27243: Update the __aiter__ protocol: instead of returning an
+  awaitable that resolves to an asynchronous iterator, the asynchronous
   iterator should be returned directly.  Doing the former will trigger a
   PendingDeprecationWarning.
 
-
 Library
 -------
 
@@ -2336,1961 +2852,1986 @@ Library
   exposed on the API which are not implemented on GNU/Hurd. They would not
   work at runtime anyway.
 
-- Issue #27025: Generated names for Tkinter widgets are now more meanful
-  and recognizirable.
+- bpo-27025: Generated names for Tkinter widgets are now more meanful and
+  recognizirable.
 
-- Issue #25455: Fixed crashes in repr of recursive ElementTree.Element and
+- bpo-25455: Fixed crashes in repr of recursive ElementTree.Element and
   functools.partial objects.
 
-- Issue #27294: Improved repr for Tkinter event objects.
+- bpo-27294: Improved repr for Tkinter event objects.
 
-- Issue #20508: Improve exception message of IPv{4,6}Network.__getitem__.
-  Patch by Gareth Rees.
+- bpo-20508: Improve exception message of IPv{4,6}Network.__getitem__. Patch
+  by Gareth Rees.
 
-- [Security] Issue #26556: Update expat to 2.1.1, fixes CVE-2015-1283.
+Security
+--------
 
-- [Security] Fix TLS stripping vulnerability in smtplib, CVE-2016-0772.
-  Reported by Team Oststrom.
+- bpo-26556: Update expat to 2.1.1, fixes CVE-2015-1283.
 
-- Issue #21386: Implement missing IPv4Address.is_global property.  It was
+- Fix TLS stripping vulnerability in smtplib, CVE-2016-0772. Reported by
+  Team Oststrom.
+
+Library
+-------
+
+- bpo-21386: Implement missing IPv4Address.is_global property.  It was
   documented since 07a5610bae9d.  Initial patch by Roger Luethi.
 
-- Issue #27029: Removed deprecated support of universal newlines mode from
+- bpo-27029: Removed deprecated support of universal newlines mode from
   ZipFile.open().
 
-- Issue #27030: Unknown escapes consisting of ``'\'`` and an ASCII letter in
+- bpo-27030: Unknown escapes consisting of ``'\'`` and an ASCII letter in
   regular expressions now are errors.  The re.LOCALE flag now can be used
   only with bytes patterns.
 
-- Issue #27186: Add os.PathLike support to DirEntry (part of PEP 519).
-  Initial patch by Jelle Zijlstra.
+- bpo-27186: Add os.PathLike support to DirEntry (part of PEP 519). Initial
+  patch by Jelle Zijlstra.
 
-- Issue #20900: distutils register command now decodes HTTP responses
+- bpo-20900: distutils register command now decodes HTTP responses
   correctly.  Initial patch by ingrid.
 
-- Issue #27186: Add os.PathLike support to pathlib, removing its provisional
+- bpo-27186: Add os.PathLike support to pathlib, removing its provisional
   status (part of PEP 519). Initial patch by Dusty Phillips.
 
-- Issue #27186: Add support for os.PathLike objects to os.fsencode() and
+- bpo-27186: Add support for os.PathLike objects to os.fsencode() and
   os.fsdecode() (part of PEP 519).
 
-- Issue #27186: Introduce os.PathLike and os.fspath() (part of PEP 519).
+- bpo-27186: Introduce os.PathLike and os.fspath() (part of PEP 519).
 
-- A new version of typing.py provides several new classes and
-  features: @overload outside stubs, Reversible, DefaultDict, Text,
-  ContextManager, Type[], NewType(), TYPE_CHECKING, and numerous bug
-  fixes (note that some of the new features are not yet implemented in
-  mypy or other static analyzers).  Also classes for PEP 492
-  (Awaitable, AsyncIterable, AsyncIterator) have been added (in fact
-  they made it into 3.5.1 but were never mentioned).
+- A new version of typing.py provides several new classes and features:
+  @overload outside stubs, Reversible, DefaultDict, Text, ContextManager,
+  Type[], NewType(), TYPE_CHECKING, and numerous bug fixes (note that some
+  of the new features are not yet implemented in mypy or other static
+  analyzers). Also classes for PEP 492 (Awaitable, AsyncIterable,
+  AsyncIterator) have been added (in fact they made it into 3.5.1 but were
+  never mentioned).
 
-- Issue #25738: Stop http.server.BaseHTTPRequestHandler.send_error() from
+- bpo-25738: Stop http.server.BaseHTTPRequestHandler.send_error() from
   sending a message body for 205 Reset Content.  Also, don't send Content
   header fields in responses that don't have a body.  Patch by Susumu
   Koshiba.
 
-- Issue #21313: Fix the "platform" module to tolerate when sys.version
-  contains truncated build information.
+- bpo-21313: Fix the "platform" module to tolerate when sys.version contains
+  truncated build information.
+
+Security
+--------
 
-- [Security] Issue #26839: On Linux, :func:`os.urandom` now calls
-  ``getrandom()`` with ``GRND_NONBLOCK`` to fall back on reading
-  ``/dev/urandom`` if the urandom entropy pool is not initialized yet. Patch
-  written by Colm Buckley.
+- bpo-26839: On Linux, :func:`os.urandom` now calls ``getrandom()`` with
+  ``GRND_NONBLOCK`` to fall back on reading ``/dev/urandom`` if the urandom
+  entropy pool is not initialized yet. Patch written by Colm Buckley.
 
-- Issue #23883: Added missing APIs to __all__ to match the documented APIs
-  for the following modules: cgi, mailbox, mimetypes, plistlib and smtpd.
+Library
+-------
+
+- bpo-23883: Added missing APIs to __all__ to match the documented APIs for
+  the following modules: cgi, mailbox, mimetypes, plistlib and smtpd.
   Patches by Jacek Kołodziej.
 
-- Issue #27164: In the zlib module, allow decompressing raw Deflate streams
+- bpo-27164: In the zlib module, allow decompressing raw Deflate streams
   with a predefined zdict.  Based on patch by Xiang Zhang.
 
-- Issue #24291: Fix wsgiref.simple_server.WSGIRequestHandler to completely
+- bpo-24291: Fix wsgiref.simple_server.WSGIRequestHandler to completely
   write data to the client.  Previously it could do partial writes and
   truncate data.  Also, wsgiref.handler.ServerHandler can now handle stdout
   doing partial writes, but this is deprecated.
 
-- Issue #21272: Use _sysconfigdata.py to initialize distutils.sysconfig.
+- bpo-21272: Use _sysconfigdata.py to initialize distutils.sysconfig.
 
-- Issue #19611: :mod:`inspect` now reports the implicit ``.0`` parameters
-  generated by the compiler for comprehension and generator expression scopes
-  as if they were positional-only parameters called ``implicit0``.
+- bpo-19611: :mod:`inspect` now reports the implicit ``.0`` parameters
+  generated by the compiler for comprehension and generator expression
+  scopes as if they were positional-only parameters called ``implicit0``.
   Patch by Jelle Zijlstra.
 
-- Issue #26809: Add ``__all__`` to :mod:`string`.  Patch by Emanuel Barry.
+- bpo-26809: Add ``__all__`` to :mod:`string`.  Patch by Emanuel Barry.
 
-- Issue #26373: subprocess.Popen.communicate now correctly ignores
-  BrokenPipeError when the child process dies before .communicate()
-  is called in more/all circumstances.
+- bpo-26373: subprocess.Popen.communicate now correctly ignores
+  BrokenPipeError when the child process dies before .communicate() is
+  called in more/all circumstances.
 
 - signal, socket, and ssl module IntEnum constant name lookups now return a
   consistent name for values having multiple names.  Ex: signal.Signals(6)
   now refers to itself as signal.SIGALRM rather than flipping between that
   and signal.SIGIOT based on the interpreter's hash randomization seed.
 
-- Issue #27167: Clarify the subprocess.CalledProcessError error message text
+- bpo-27167: Clarify the subprocess.CalledProcessError error message text
   when the child process died due to a signal.
 
-- Issue #25931: Don't define socketserver.Forking* names on platforms such
-  as Windows that do not support os.fork().
+- bpo-25931: Don't define socketserver.Forking* names on platforms such as
+  Windows that do not support os.fork().
 
-- Issue #21776: distutils.upload now correctly handles HTTPError.
-  Initial patch by Claudiu Popa.
+- bpo-21776: distutils.upload now correctly handles HTTPError. Initial patch
+  by Claudiu Popa.
 
-- Issue #26526: Replace custom parse tree validation in the parser
-  module with a simple DFA validator.
+- bpo-26526: Replace custom parse tree validation in the parser module with
+  a simple DFA validator.
 
-- Issue #27114: Fix SSLContext._load_windows_store_certs fails with
+- bpo-27114: Fix SSLContext._load_windows_store_certs fails with
   PermissionError
 
-- Issue #18383: Avoid creating duplicate filters when using filterwarnings
-  and simplefilter.  Based on patch by Alex Shkop.
+- bpo-18383: Avoid creating duplicate filters when using filterwarnings and
+  simplefilter. Based on patch by Alex Shkop.
 
-- Issue #23026: winreg.QueryValueEx() now return an integer for REG_QWORD type.
+- bpo-23026: winreg.QueryValueEx() now return an integer for REG_QWORD type.
 
-- Issue #26741: subprocess.Popen destructor now emits a ResourceWarning warning
+- bpo-26741: subprocess.Popen destructor now emits a ResourceWarning warning
   if the child process is still running.
 
-- Issue #27056: Optimize pickle.load() and pickle.loads(), up to 10% faster
-  to deserialize a lot of small objects.
+- bpo-27056: Optimize pickle.load() and pickle.loads(), up to 10% faster to
+  deserialize a lot of small objects.
 
-- Issue #21271: New keyword only parameters in reset_mock call.
+- bpo-21271: New keyword only parameters in reset_mock call.
 
 IDLE
 ----
 
-- Issue #5124: Paste with text selected now replaces the selection on X11.
-  This matches how paste works on Windows, Mac, most modern Linux apps,
-  and ttk widgets.  Original patch by Serhiy Storchaka.
+- bpo-5124: Paste with text selected now replaces the selection on X11. This
+  matches how paste works on Windows, Mac, most modern Linux apps, and ttk
+  widgets. Original patch by Serhiy Storchaka.
 
-- Issue #24750: Switch all scrollbars in IDLE to ttk versions.
-  Where needed, minimal tests are added to cover changes.
+- bpo-24750: Switch all scrollbars in IDLE to ttk versions. Where needed,
+  minimal tests are added to cover changes.
 
-- Issue #24759: IDLE requires tk 8.5 and availability ttk widgets.
-  Delete now unneeded tk version tests and code for older versions.
-  Add test for IDLE syntax colorizoer.
+- bpo-24759: IDLE requires tk 8.5 and availability ttk widgets. Delete now
+  unneeded tk version tests and code for older versions. Add test for IDLE
+  syntax colorizoer.
 
-- Issue #27239: idlelib.macosx.isXyzTk functions initialize as needed.
+- bpo-27239: idlelib.macosx.isXyzTk functions initialize as needed.
 
-- Issue #27262: move Aqua unbinding code, which enable context menus, to maxosx.
+- bpo-27262: move Aqua unbinding code, which enable context menus, to
+  maxosx.
 
-- Issue #24759: Make clear in idlelib.idle_test.__init__ that the directory
-  is a private implementation of test.test_idle and tool for maintainers.
+- bpo-24759: Make clear in idlelib.idle_test.__init__ that the directory is
+  a private implementation of test.test_idle and tool for maintainers.
 
-- Issue #27196: Stop 'ThemeChanged' warnings when running IDLE tests.
-  These persisted after other warnings were suppressed in #20567.
-  Apply Serhiy Storchaka's update_idletasks solution to four test files.
-  Record this additional advice in idle_test/README.txt
+- bpo-27196: Stop 'ThemeChanged' warnings when running IDLE tests. These
+  persisted after other warnings were suppressed in #20567. Apply Serhiy
+  Storchaka's update_idletasks solution to four test files. Record this
+  additional advice in idle_test/README.txt
 
-- Issue #20567: Revise idle_test/README.txt with advice about avoiding
-  tk warning messages from tests.  Apply advice to several IDLE tests.
+- bpo-20567: Revise idle_test/README.txt with advice about avoiding tk
+  warning messages from tests.  Apply advice to several IDLE tests.
 
-- Issue #24225: Update idlelib/README.txt with new file names
-  and event handlers.
+- bpo-24225: Update idlelib/README.txt with new file names and event
+  handlers.
 
-- Issue #27156: Remove obsolete code not used by IDLE.  Replacements:
-  1. help.txt, replaced by help.html, is out-of-date and should not be used.
-  Its dedicated viewer has be replaced by the html viewer in help.py.
-  2. ``import idlever; I = idlever.IDLE_VERSION`` is the same as
-  ``import sys; I = version[:version.index(' ')]``
-  3. After ``ob = stackviewer.VariablesTreeItem(*args)``,
-  ``ob.keys() == list(ob.object.keys)``.
-  4. In macosc, runningAsOSXAPP == isAquaTk; idCarbonAquaTk == isCarbonTk
+- bpo-27156: Remove obsolete code not used by IDLE.
 
-- Issue #27117: Make colorizer htest and turtledemo work with dark themes.
-  Move code for configuring text widget colors to a new function.
+- bpo-27117: Make colorizer htest and turtledemo work with dark themes. Move
+  code for configuring text widget colors to a new function.
 
-- Issue #24225: Rename many `idlelib/*.py` and `idle_test/test_*.py` files.
-  Edit files to replace old names with new names when the old name
-  referred to the module rather than the class it contained.
-  See the issue and IDLE section in What's New in 3.6 for more.
+- bpo-24225: Rename many `idlelib/*.py` and `idle_test/test_*.py` files.
+  Edit files to replace old names with new names when the old name referred
+  to the module rather than the class it contained. See the issue and IDLE
+  section in What's New in 3.6 for more.
 
-- Issue #26673: When tk reports font size as 0, change to size 10.
-  Such fonts on Linux prevented the configuration dialog from opening.
+- bpo-26673: When tk reports font size as 0, change to size 10. Such fonts
+  on Linux prevented the configuration dialog from opening.
 
-- Issue #21939: Add test for IDLE's percolator.
-  Original patch by Saimadhav Heblikar.
+- bpo-21939: Add test for IDLE's percolator. Original patch by Saimadhav
+  Heblikar.
 
-- Issue #21676: Add test for IDLE's replace dialog.
-  Original patch by Saimadhav Heblikar.
+- bpo-21676: Add test for IDLE's replace dialog. Original patch by Saimadhav
+  Heblikar.
 
-- Issue #18410: Add test for IDLE's search dialog.
-  Original patch by Westley Martínez.
+- bpo-18410: Add test for IDLE's search dialog. Original patch by Westley
+  Martínez.
 
-- Issue #21703: Add test for undo delegator.  Patch mostly by
-  Saimadhav Heblikar .
+- bpo-21703: Add test for undo delegator.  Patch mostly by Saimadhav
+  Heblikar .
 
-- Issue #27044: Add ConfigDialog.remove_var_callbacks to stop memory leaks.
+- bpo-27044: Add ConfigDialog.remove_var_callbacks to stop memory leaks.
 
-- Issue #23977: Add more asserts to test_delegator.
+- bpo-23977: Add more asserts to test_delegator.
 
 Documentation
 -------------
 
-- Issue #16484: Change the default PYTHONDOCS URL to "https:", and fix the
+- bpo-16484: Change the default PYTHONDOCS URL to "https:", and fix the
   resulting links to use lowercase.  Patch by Sean Rodman, test by Kaushik
   Nadikuditi.
 
-- Issue #24136: Document the new PEP 448 unpacking syntax of 3.5.
+- bpo-24136: Document the new PEP 448 unpacking syntax of 3.5.
 
-- Issue #22558: Add remaining doc links to source code for Python-coded modules.
-  Patch by Yoni Lavi.
+- bpo-22558: Add remaining doc links to source code for Python-coded
+  modules. Patch by Yoni Lavi.
 
 Tests
 -----
 
-- Issue #25285: regrtest now uses subprocesses when the -j1 command line option
-  is used: each test file runs in a fresh child process. Before, the -j1 option
-  was ignored.
+- bpo-25285: regrtest now uses subprocesses when the -j1 command line option
+  is used: each test file runs in a fresh child process. Before, the -j1
+  option was ignored.
 
-- Issue #25285: Tools/buildbot/test.bat script now uses -j1 by default to run
+- bpo-25285: Tools/buildbot/test.bat script now uses -j1 by default to run
   each test file in fresh child process.
 
 Windows
 -------
 
-- Issue #27064: The py.exe launcher now defaults to Python 3.
-  The Windows launcher ``py.exe`` no longer prefers an installed
-  Python 2 version over Python 3 by default when used interactively.
+- bpo-27064: The py.exe launcher now defaults to Python 3. The Windows
+  launcher ``py.exe`` no longer prefers an installed Python 2 version over
+  Python 3 by default when used interactively.
 
 Build
 -----
 
-- Issue #27229: Fix the cross-compiling pgen rule for in-tree builds.  Patch
-  by Xavier de Gaye.
+- bpo-27229: Fix the cross-compiling pgen rule for in-tree builds.  Patch by
+  Xavier de Gaye.
 
-- Issue #26930: Update OS X 10.5+ 32-bit-only installer to build
-  and link with OpenSSL 1.0.2h.
+- bpo-26930: Update OS X 10.5+ 32-bit-only installer to build and link with
+  OpenSSL 1.0.2h.
 
-Misc
-----
+Windows
+-------
 
-- Issue #17500, and https://github.com/python/pythondotorg/issues/945: Remove
-  unused and outdated icons.
+- bpo-17500: Remove unused and outdated icons. (See also:
+  https://github.com/python/pythondotorg/issues/945)
 
 C API
 -----
 
-- Issue #27186: Add the PyOS_FSPath() function (part of PEP 519).
+- bpo-27186: Add the PyOS_FSPath() function (part of PEP 519).
 
-- Issue #26282: PyArg_ParseTupleAndKeywords() now supports positional-only
+- bpo-26282: PyArg_ParseTupleAndKeywords() now supports positional-only
   parameters.
 
 Tools/Demos
 -----------
 
-- Issue #26282: Argument Clinic now supports positional-only and keyword
+- bpo-26282: Argument Clinic now supports positional-only and keyword
   parameters in the same function.
 
 
 What's New in Python 3.6.0 alpha 1?
 ===================================
 
-Release date: 2016-05-16
+*Release date: 2016-05-16*
 
 Core and Builtins
 -----------------
 
-- Issue #20041: Fixed TypeError when frame.f_trace is set to None.
-  Patch by Xavier de Gaye.
+- bpo-20041: Fixed TypeError when frame.f_trace is set to None. Patch by
+  Xavier de Gaye.
 
-- Issue #26168: Fixed possible refleaks in failing Py_BuildValue() with the "N"
+- bpo-26168: Fixed possible refleaks in failing Py_BuildValue() with the "N"
   format unit.
 
-- Issue #26991: Fix possible refleak when creating a function with annotations.
+- bpo-26991: Fix possible refleak when creating a function with annotations.
 
-- Issue #27039: Fixed bytearray.remove() for values greater than 127.  Based on
+- bpo-27039: Fixed bytearray.remove() for values greater than 127.  Based on
   patch by Joe Jevnik.
 
-- Issue #23640: int.from_bytes() no longer bypasses constructors for subclasses.
+- bpo-23640: int.from_bytes() no longer bypasses constructors for
+  subclasses.
 
-- Issue #27005: Optimized the float.fromhex() class method for exact float.
-  It is now 2 times faster.
+- bpo-27005: Optimized the float.fromhex() class method for exact float. It
+  is now 2 times faster.
 
-- Issue #18531: Single var-keyword argument of dict subtype was passed
+- bpo-18531: Single var-keyword argument of dict subtype was passed
   unscathed to the C-defined function.  Now it is converted to exact dict.
 
-- Issue #26811: gc.get_objects() no longer contains a broken tuple with NULL
+- bpo-26811: gc.get_objects() no longer contains a broken tuple with NULL
   pointer.
 
-- Issue #20120: Use RawConfigParser for .pypirc parsing,
-  removing support for interpolation unintentionally added
-  with move to Python 3. Behavior no longer does any
-  interpolation in .pypirc files, matching behavior in Python
-  2.7 and Setuptools 19.0.
+- bpo-20120: Use RawConfigParser for .pypirc parsing, removing support for
+  interpolation unintentionally added with move to Python 3. Behavior no
+  longer does any interpolation in .pypirc files, matching behavior in
+  Python 2.7 and Setuptools 19.0.
 
-- Issue #26249: Memory functions of the :c:func:`PyMem_Malloc` domain
-  (:c:data:`PYMEM_DOMAIN_MEM`) now use the :ref:`pymalloc allocator <pymalloc>`
-  rather than system :c:func:`malloc`. Applications calling
+- bpo-26249: Memory functions of the :c:func:`PyMem_Malloc` domain
+  (:c:data:`PYMEM_DOMAIN_MEM`) now use the :ref:`pymalloc allocator
+  <pymalloc>` rather than system :c:func:`malloc`. Applications calling
   :c:func:`PyMem_Malloc` without holding the GIL can now crash: use
-  ``PYTHONMALLOC=debug`` environment variable to validate the usage of memory
-  allocators in your application.
+  ``PYTHONMALLOC=debug`` environment variable to validate the usage of
+  memory allocators in your application.
 
-- Issue #26802: Optimize function calls only using unpacking like
+- bpo-26802: Optimize function calls only using unpacking like
   ``func(*tuple)`` (no other positional argument, no keyword): avoid copying
   the tuple. Patch written by Joe Jevnik.
 
-- Issue #26659: Make the builtin slice type support cycle collection.
+- bpo-26659: Make the builtin slice type support cycle collection.
 
-- Issue #26718: super.__init__ no longer leaks memory if called multiple times.
+- bpo-26718: super.__init__ no longer leaks memory if called multiple times.
   NOTE: A direct call of super.__init__ is not endorsed!
 
-- Issue #27138: Fix the doc comment for FileFinder.find_spec().
+- bpo-27138: Fix the doc comment for FileFinder.find_spec().
 
-- Issue #27147: Mention PEP 420 in the importlib docs.
+- bpo-27147: Mention PEP 420 in the importlib docs.
 
-- Issue #25339: PYTHONIOENCODING now has priority over locale in setting the
+- bpo-25339: PYTHONIOENCODING now has priority over locale in setting the
   error handler for stdin and stdout.
 
-- Issue #26494: Fixed crash on iterating exhausting iterators.
-  Affected classes are generic sequence iterators, iterators of str, bytes,
-  bytearray, list, tuple, set, frozenset, dict, OrderedDict, corresponding
-  views and os.scandir() iterator.
+- bpo-26494: Fixed crash on iterating exhausting iterators. Affected classes
+  are generic sequence iterators, iterators of str, bytes, bytearray, list,
+  tuple, set, frozenset, dict, OrderedDict, corresponding views and
+  os.scandir() iterator.
 
-- Issue #26574: Optimize ``bytes.replace(b'', b'.')`` and
+- bpo-26574: Optimize ``bytes.replace(b'', b'.')`` and
   ``bytearray.replace(b'', b'.')``. Patch written by Josh Snider.
 
-- Issue #26581: If coding cookie is specified multiple times on a line in
+- bpo-26581: If coding cookie is specified multiple times on a line in
   Python source code file, only the first one is taken to account.
 
-- Issue #19711: Add tests for reloading namespace packages.
+- bpo-19711: Add tests for reloading namespace packages.
 
-- Issue #21099: Switch applicable importlib tests to use PEP 451 API.
+- bpo-21099: Switch applicable importlib tests to use PEP 451 API.
 
-- Issue #26563: Debug hooks on Python memory allocators now raise a fatal
-  error if functions of the :c:func:`PyMem_Malloc` family are called without
+- bpo-26563: Debug hooks on Python memory allocators now raise a fatal error
+  if functions of the :c:func:`PyMem_Malloc` family are called without
   holding the GIL.
 
-- Issue #26564: On error, the debug hooks on Python memory allocators now use
-  the :mod:`tracemalloc` module to get the traceback where a memory block was
-  allocated.
+- bpo-26564: On error, the debug hooks on Python memory allocators now use
+  the :mod:`tracemalloc` module to get the traceback where a memory block
+  was allocated.
 
-- Issue #26558: The debug hooks on Python memory allocator
+- bpo-26558: The debug hooks on Python memory allocator
   :c:func:`PyObject_Malloc` now detect when functions are called without
   holding the GIL.
 
-- Issue #26516: Add :envvar:`PYTHONMALLOC` environment variable to set the
+- bpo-26516: Add :envvar:`PYTHONMALLOC` environment variable to set the
   Python memory allocators and/or install debug hooks.
 
-- Issue #26516: The :c:func:`PyMem_SetupDebugHooks` function can now also be
+- bpo-26516: The :c:func:`PyMem_SetupDebugHooks` function can now also be
   used on Python compiled in release mode.
 
-- Issue #26516: The :envvar:`PYTHONMALLOCSTATS` environment variable can now
+- bpo-26516: The :envvar:`PYTHONMALLOCSTATS` environment variable can now
   also be used on Python compiled in release mode. It now has no effect if
   set to an empty string.
 
-- Issue #26516: In debug mode, debug hooks are now also installed on Python
+- bpo-26516: In debug mode, debug hooks are now also installed on Python
   memory allocators when Python is configured without pymalloc.
 
-- Issue #26464: Fix str.translate() when string is ASCII and first replacements
+- bpo-26464: Fix str.translate() when string is ASCII and first replacements
   removes character, but next replacement uses a non-ASCII character or a
   string longer than 1 character. Regression introduced in Python 3.5.0.
 
-- Issue #22836: Ensure exception reports from PyErr_Display() and
+- bpo-22836: Ensure exception reports from PyErr_Display() and
   PyErr_WriteUnraisable() are sensible even when formatting them produces
   secondary errors.  This affects the reports produced by
   sys.__excepthook__() and when __del__() raises an exception.
 
-- Issue #26302: Correct behavior to reject comma as a legal character for
+- bpo-26302: Correct behavior to reject comma as a legal character for
   cookie names.
 
-- Issue #26136: Upgrade the warning when a generator raises StopIteration
-  from PendingDeprecationWarning to DeprecationWarning.  Patch by Anish
-  Shah.
+- bpo-26136: Upgrade the warning when a generator raises StopIteration from
+  PendingDeprecationWarning to DeprecationWarning.  Patch by Anish Shah.
 
-- Issue #26204: The compiler now ignores all constant statements: bytes, str,
-  int, float, complex, name constants (None, False, True), Ellipsis
-  and ast.Constant; not only str and int. For example, ``1.0`` is now ignored
-  in ``def f(): 1.0``.
+- bpo-26204: The compiler now ignores all constant statements: bytes, str,
+  int, float, complex, name constants (None, False, True), Ellipsis and
+  ast.Constant; not only str and int. For example, ``1.0`` is now ignored in
+  ``def f(): 1.0``.
 
-- Issue #4806: Avoid masking the original TypeError exception when using star
-  (``*``) unpacking in function calls.  Based on patch by Hagen Fürstenau and
-  Daniel Urban.
+- bpo-4806: Avoid masking the original TypeError exception when using star
+  (``*``) unpacking in function calls.  Based on patch by Hagen Fürstenau
+  and Daniel Urban.
 
-- Issue #26146: Add a new kind of AST node: ``ast.Constant``. It can be used
-  by external AST optimizers, but the compiler does not emit directly such
+- bpo-26146: Add a new kind of AST node: ``ast.Constant``. It can be used by
+  external AST optimizers, but the compiler does not emit directly such
   node.
 
-- Issue #23601:  Sped-up allocation of dict key objects by using Python's
-  small object allocator.  (Contributed by Julian Taylor.)
+- bpo-23601: Sped-up allocation of dict key objects by using Python's small
+  object allocator.  (Contributed by Julian Taylor.)
 
-- Issue #18018: Import raises ImportError instead of SystemError if a relative
+- bpo-18018: Import raises ImportError instead of SystemError if a relative
   import is attempted without a known parent package.
 
-- Issue #25843: When compiling code, don't merge constants if they are equal
-  but have a different types. For example, ``f1, f2 = lambda: 1, lambda: 1.0``
-  is now correctly compiled to two different functions: ``f1()`` returns ``1``
-  (``int``) and ``f2()`` returns ``1.0`` (``float``), even if ``1`` and ``1.0``
-  are equal.
+- bpo-25843: When compiling code, don't merge constants if they are equal
+  but have a different types. For example, ``f1, f2 = lambda: 1, lambda:
+  1.0`` is now correctly compiled to two different functions: ``f1()``
+  returns ``1`` (``int``) and ``f2()`` returns ``1.0`` (``float``), even if
+  ``1`` and ``1.0`` are equal.
 
-- Issue #26107: The format of the ``co_lnotab`` attribute of code objects
+- bpo-26107: The format of the ``co_lnotab`` attribute of code objects
   changes to support negative line number delta.
 
-- Issue #26154: Add a new private _PyThreadState_UncheckedGet() function to get
-  the current Python thread state, but don't issue a fatal error if it is NULL.
-  This new function must be used instead of accessing directly the
+- bpo-26154: Add a new private _PyThreadState_UncheckedGet() function to get
+  the current Python thread state, but don't issue a fatal error if it is
+  NULL. This new function must be used instead of accessing directly the
   _PyThreadState_Current variable.  The variable is no more exposed since
   Python 3.5.1 to hide the exact implementation of atomic C types, to avoid
   compiler issues.
 
-- Issue #25791: If __package__ != __spec__.parent or if neither __package__ or
+- bpo-25791: If __package__ != __spec__.parent or if neither __package__ or
   __spec__ are defined then ImportWarning is raised.
 
-- Issue #22995: [UPDATE] Comment out the one of the pickleability tests in
+- bpo-22995: [UPDATE] Comment out the one of the pickleability tests in
   _PyObject_GetState() due to regressions observed in Cython-based projects.
 
-- Issue #25961: Disallowed null characters in the type name.
+- bpo-25961: Disallowed null characters in the type name.
 
-- Issue #25973: Fix segfault when an invalid nonlocal statement binds a name
+- bpo-25973: Fix segfault when an invalid nonlocal statement binds a name
   starting with two underscores.
 
-- Issue #22995: Instances of extension types with a state that aren't
+- bpo-22995: Instances of extension types with a state that aren't
   subclasses of list or dict and haven't implemented any pickle-related
-  methods (__reduce__, __reduce_ex__, __getnewargs__, __getnewargs_ex__,
-  or __getstate__), can no longer be pickled.  Including memoryview.
+  methods (__reduce__, __reduce_ex__, __getnewargs__, __getnewargs_ex__, or
+  __getstate__), can no longer be pickled.  Including memoryview.
 
-- Issue #20440: Massive replacing unsafe attribute setting code with special
+- bpo-20440: Massive replacing unsafe attribute setting code with special
   macro Py_SETREF.
 
-- Issue #25766: Special method __bytes__() now works in str subclasses.
+- bpo-25766: Special method __bytes__() now works in str subclasses.
 
-- Issue #25421: __sizeof__ methods of builtin types now use dynamic basic size.
+- bpo-25421: __sizeof__ methods of builtin types now use dynamic basic size.
   This allows sys.getsize() to work correctly with their subclasses with
   __slots__ defined.
 
-- Issue #25709: Fixed problem with in-place string concatenation and utf-8
+- bpo-25709: Fixed problem with in-place string concatenation and utf-8
   cache.
 
-- Issue #5319: New Py_FinalizeEx() API allowing Python to set an exit status
-  of 120 on failure to flush buffered streams.
+- bpo-5319: New Py_FinalizeEx() API allowing Python to set an exit status of
+  120 on failure to flush buffered streams.
 
-- Issue #25485: telnetlib.Telnet is now a context manager.
+- bpo-25485: telnetlib.Telnet is now a context manager.
 
-- Issue #24097: Fixed crash in object.__reduce__() if slot name is freed inside
+- bpo-24097: Fixed crash in object.__reduce__() if slot name is freed inside
   __getattr__.
 
-- Issue #24731: Fixed crash on converting objects with special methods
+- bpo-24731: Fixed crash on converting objects with special methods
   __bytes__, __trunc__, and __float__ returning instances of subclasses of
-  bytes, int, and float to subclasses of bytes, int, and float correspondingly.
+  bytes, int, and float to subclasses of bytes, int, and float
+  correspondingly.
 
-- Issue #25630: Fix a possible segfault during argument parsing in functions
+- bpo-25630: Fix a possible segfault during argument parsing in functions
   that accept filesystem paths.
 
-- Issue #23564: Fixed a partially broken sanity check in the _posixsubprocess
+- bpo-23564: Fixed a partially broken sanity check in the _posixsubprocess
   internals regarding how fds_to_pass were passed to the child.  The bug had
   no actual impact as subprocess.py already avoided it.
 
-- Issue #25388: Fixed tokenizer crash when processing undecodable source code
+- bpo-25388: Fixed tokenizer crash when processing undecodable source code
   with a null byte.
 
-- Issue #25462: The hash of the key now is calculated only once in most
+- bpo-25462: The hash of the key now is calculated only once in most
   operations in C implementation of OrderedDict.
 
-- Issue #22995: Default implementation of __reduce__ and __reduce_ex__ now
+- bpo-22995: Default implementation of __reduce__ and __reduce_ex__ now
   rejects builtin types with not defined __new__.
 
-- Issue #24802: Avoid buffer overreads when int(), float(), compile(), exec()
+- bpo-24802: Avoid buffer overreads when int(), float(), compile(), exec()
   and eval() are passed bytes-like objects.  These objects are not
-  necessarily terminated by a null byte, but the functions assumed they were.
+  necessarily terminated by a null byte, but the functions assumed they
+  were.
 
-- Issue #25555: Fix parser and AST: fill lineno and col_offset of "arg" node
+- bpo-25555: Fix parser and AST: fill lineno and col_offset of "arg" node
   when compiling AST from Python objects.
 
-- Issue #24726: Fixed a crash and leaking NULL in repr() of OrderedDict that
+- bpo-24726: Fixed a crash and leaking NULL in repr() of OrderedDict that
   was mutated by direct calls of dict methods.
 
-- Issue #25449: Iterating OrderedDict with keys with unstable hash now raises
+- bpo-25449: Iterating OrderedDict with keys with unstable hash now raises
   KeyError in C implementations as well as in Python implementation.
 
-- Issue #25395: Fixed crash when highly nested OrderedDict structures were
+- bpo-25395: Fixed crash when highly nested OrderedDict structures were
   garbage collected.
 
-- Issue #25401: Optimize bytes.fromhex() and bytearray.fromhex(): they are now
+- bpo-25401: Optimize bytes.fromhex() and bytearray.fromhex(): they are now
   between 2x and 3.5x faster.
 
-- Issue #25399: Optimize bytearray % args using the new private _PyBytesWriter
+- bpo-25399: Optimize bytearray % args using the new private _PyBytesWriter
   API. Formatting is now between 2.5 and 5 times faster.
 
-- Issue #25274: sys.setrecursionlimit() now raises a RecursionError if the new
-  recursion limit is too low depending at the current recursion depth. Modify
-  also the "lower-water mark" formula to make it monotonic. This mark is used
-  to decide when the overflowed flag of the thread state is reset.
+- bpo-25274: sys.setrecursionlimit() now raises a RecursionError if the new
+  recursion limit is too low depending at the current recursion depth.
+  Modify also the "lower-water mark" formula to make it monotonic. This mark
+  is used to decide when the overflowed flag of the thread state is reset.
 
-- Issue #24402: Fix input() to prompt to the redirected stdout when
+- bpo-24402: Fix input() to prompt to the redirected stdout when
   sys.stdout.fileno() fails.
 
-- Issue #25349: Optimize bytes % args using the new private _PyBytesWriter API.
+- bpo-25349: Optimize bytes % args using the new private _PyBytesWriter API.
   Formatting is now up to 2 times faster.
 
-- Issue #24806: Prevent builtin types that are not allowed to be subclassed from
-  being subclassed through multiple inheritance.
+- bpo-24806: Prevent builtin types that are not allowed to be subclassed
+  from being subclassed through multiple inheritance.
 
-- Issue #25301: The UTF-8 decoder is now up to 15 times as fast for error
+- bpo-25301: The UTF-8 decoder is now up to 15 times as fast for error
   handlers: ``ignore``, ``replace`` and ``surrogateescape``.
 
-- Issue #24848: Fixed a number of bugs in UTF-7 decoding of misformed data.
+- bpo-24848: Fixed a number of bugs in UTF-7 decoding of misformed data.
 
-- Issue #25267: The UTF-8 encoder is now up to 75 times as fast for error
+- bpo-25267: The UTF-8 encoder is now up to 75 times as fast for error
   handlers: ``ignore``, ``replace``, ``surrogateescape``, ``surrogatepass``.
-  Patch co-written with Serhiy Storchaka.
+  Patch co- written with Serhiy Storchaka.
 
-- Issue #25280: Import trace messages emitted in verbose (-v) mode are no
+- bpo-25280: Import trace messages emitted in verbose (-v) mode are no
   longer formatted twice.
 
-- Issue #25227: Optimize ASCII and latin1 encoders with the ``surrogateescape``
+- bpo-25227: Optimize ASCII and latin1 encoders with the ``surrogateescape``
   error handler: the encoders are now up to 3 times as fast. Initial patch
   written by Serhiy Storchaka.
 
-- Issue #25003: On Solaris 11.3 or newer, os.urandom() now uses the
-  getrandom() function instead of the getentropy() function. The getentropy()
-  function is blocking to generate very good quality entropy, os.urandom()
-  doesn't need such high-quality entropy.
+- bpo-25003: On Solaris 11.3 or newer, os.urandom() now uses the getrandom()
+  function instead of the getentropy() function. The getentropy() function
+  is blocking to generate very good quality entropy, os.urandom() doesn't
+  need such high- quality entropy.
 
-- Issue #9232: Modify Python's grammar to allow trailing commas in the
-  argument list of a function declaration.  For example, "def f(\*, a =
-  3,): pass" is now legal. Patch from Mark Dickinson.
+- bpo-9232: Modify Python's grammar to allow trailing commas in the argument
+  list of a function declaration.  For example, "def f(\*, a = 3,): pass" is
+  now legal. Patch from Mark Dickinson.
 
-- Issue #24965: Implement PEP 498 "Literal String Interpolation". This
-  allows you to embed expressions inside f-strings, which are
-  converted to normal strings at run time. Given x=3, then
-  f'value={x}' == 'value=3'. Patch by Eric V. Smith.
+- bpo-24965: Implement PEP 498 "Literal String Interpolation". This allows
+  you to embed expressions inside f-strings, which are converted to normal
+  strings at run time. Given x=3, then f'value={x}' == 'value=3'. Patch by
+  Eric V. Smith.
 
-- Issue #26478: Fix semantic bugs when using binary operators with dictionary
+- bpo-26478: Fix semantic bugs when using binary operators with dictionary
   views and tuples.
 
-- Issue #26171: Fix possible integer overflow and heap corruption in
+- bpo-26171: Fix possible integer overflow and heap corruption in
   zipimporter.get_data().
 
-- Issue #25660: Fix TAB key behaviour in REPL with readline.
+- bpo-25660: Fix TAB key behaviour in REPL with readline.
 
-- Issue #26288: Optimize PyLong_AsDouble.
+- bpo-26288: Optimize PyLong_AsDouble.
 
-- Issues #26289 and #26315: Optimize floor and modulo division for
-  single-digit longs.  Microbenchmarks show 2-2.5x improvement.  Built-in
-  'divmod' function is now also ~10% faster.
+- bpo-26289: Optimize floor and modulo division for single-digit longs.
+  Microbenchmarks show 2-2.5x improvement.  Built-in 'divmod' function is
+  now also ~10% faster. (See also: bpo-26315)
 
-- Issue #25887: Raise a RuntimeError when a coroutine object is awaited
-  more than once.
+- bpo-25887: Raise a RuntimeError when a coroutine object is awaited more
+  than once.
 
 Library
 -------
 
-- Issue #27057: Fix os.set_inheritable() on Android, ioctl() is blocked by
+- bpo-27057: Fix os.set_inheritable() on Android, ioctl() is blocked by
   SELinux and fails with EACCESS. The function now falls back to fcntl().
   Patch written by Michał Bednarski.
 
-- Issue #27014: Fix infinite recursion using typing.py.  Thanks to Kalle Tuure!
+- bpo-27014: Fix infinite recursion using typing.py.  Thanks to Kalle Tuure!
 
-- Issue #27031: Removed dummy methods in Tkinter widget classes: tk_menuBar()
+- bpo-27031: Removed dummy methods in Tkinter widget classes: tk_menuBar()
   and tk_bindForTraversal().
 
-- Issue #14132: Fix urllib.request redirect handling when the target only has
-  query string.  Original fix by Ján Janech.
+- bpo-14132: Fix urllib.request redirect handling when the target only has a
+  query string.  Original fix by Ján Janech.
 
-- Issue #17214: The "urllib.request" module now percent-encodes non-ASCII
-  bytes found in redirect target URLs.  Some servers send Location header
-  fields with non-ASCII bytes, but "http.client" requires the request target
-  to be ASCII-encodable, otherwise a UnicodeEncodeError is raised.  Based on
+- bpo-17214: The "urllib.request" module now percent-encodes non-ASCII bytes
+  found in redirect target URLs.  Some servers send Location header fields
+  with non- ASCII bytes, but "http.client" requires the request target to be
+  ASCII- encodable, otherwise a UnicodeEncodeError is raised.  Based on
   patch by Christian Heimes.
 
-- Issue #27033: The default value of the decode_data parameter for
+- bpo-27033: The default value of the decode_data parameter for
   smtpd.SMTPChannel and smtpd.SMTPServer constructors is changed to False.
 
-- Issue #27034: Removed deprecated class asynchat.fifo.
+- bpo-27034: Removed deprecated class asynchat.fifo.
 
-- Issue #26870: Added readline.set_auto_history(), which can stop entries
-  being automatically added to the history list.  Based on patch by Tyler
+- bpo-26870: Added readline.set_auto_history(), which can stop entries being
+  automatically added to the history list.  Based on patch by Tyler
   Crompton.
 
-- Issue #26039: zipfile.ZipFile.open() can now be used to write data into a ZIP
+- bpo-26039: zipfile.ZipFile.open() can now be used to write data into a ZIP
   file, as well as for extracting data.  Patch by Thomas Kluyver.
 
-- Issue #26892: Honor debuglevel flag in urllib.request.HTTPHandler. Patch
+- bpo-26892: Honor debuglevel flag in urllib.request.HTTPHandler. Patch
   contributed by Chi Hsuan Yen.
 
-- Issue #22274: In the subprocess module, allow stderr to be redirected to
+- bpo-22274: In the subprocess module, allow stderr to be redirected to
   stdout even when stdout is not redirected.  Patch by Akira Li.
 
-- Issue #26807: mock_open 'files' no longer error on readline at end of file.
+- bpo-26807: mock_open 'files' no longer error on readline at end of file.
   Patch from Yolanda Robla.
 
-- Issue #25745: Fixed leaking a userptr in curses panel destructor.
+- bpo-25745: Fixed leaking a userptr in curses panel destructor.
 
-- Issue #26977: Removed unnecessary, and ignored, call to sum of squares helper
+- bpo-26977: Removed unnecessary, and ignored, call to sum of squares helper
   in statistics.pvariance.
 
-- Issue #26002: Use bisect in statistics.median instead of a linear search.
+- bpo-26002: Use bisect in statistics.median instead of a linear search.
   Patch by Upendra Kuma.
 
-- Issue #25974: Make use of new Decimal.as_integer_ratio() method in statistics
+- bpo-25974: Make use of new Decimal.as_integer_ratio() method in statistics
   module. Patch by Stefan Krah.
 
-- Issue #26996: Add secrets module as described in PEP 506.
+- bpo-26996: Add secrets module as described in PEP 506.
 
-- Issue #26881: The modulefinder module now supports extended opcode arguments.
+- bpo-26881: The modulefinder module now supports extended opcode arguments.
 
-- Issue #23815: Fixed crashes related to directly created instances of types in
+- bpo-23815: Fixed crashes related to directly created instances of types in
   _tkinter and curses.panel modules.
 
-- Issue #17765: weakref.ref() no longer silently ignores keyword arguments.
+- bpo-17765: weakref.ref() no longer silently ignores keyword arguments.
   Patch by Georg Brandl.
 
-- Issue #26873: xmlrpc now raises ResponseError on unsupported type tags
+- bpo-26873: xmlrpc now raises ResponseError on unsupported type tags
   instead of silently return incorrect result.
 
-- Issue #26915:  The __contains__ methods in the collections ABCs now check
-  for identity before checking equality.  This better matches the behavior
-  of the concrete classes, allows sensible handling of NaNs, and makes it
+- bpo-26915: The __contains__ methods in the collections ABCs now check for
+  identity before checking equality.  This better matches the behavior of
+  the concrete classes, allows sensible handling of NaNs, and makes it
   easier to reason about container invariants.
 
-- Issue #26711: Fixed the comparison of plistlib.Data with other types.
+- bpo-26711: Fixed the comparison of plistlib.Data with other types.
 
-- Issue #24114: Fix an uninitialized variable in `ctypes.util`.
+- bpo-24114: Fix an uninitialized variable in `ctypes.util`.
 
-  The bug only occurs on SunOS when the ctypes implementation searches
-  for the `crle` program.  Patch by Xiang Zhang.  Tested on SunOS by
-  Kees Bos.
+  The bug only occurs on SunOS when the ctypes implementation searches for
+  the `crle` program.  Patch by Xiang Zhang.  Tested on SunOS by Kees Bos.
 
-- Issue #26864: In urllib.request, change the proxy bypass host checking
+- bpo-26864: In urllib.request, change the proxy bypass host checking
   against no_proxy to be case-insensitive, and to not match unrelated host
   names that happen to have a bypassed hostname as a suffix.  Patch by Xiang
   Zhang.
 
-- Issue #24902: Print server URL on http.server startup.  Initial patch by
+- bpo-24902: Print server URL on http.server startup.  Initial patch by
   Felix Kaiser.
 
-- Issue #25788: fileinput.hook_encoded() now supports an "errors" argument
-  for passing to open.  Original patch by Joseph Hackman.
+- bpo-25788: fileinput.hook_encoded() now supports an "errors" argument for
+  passing to open.  Original patch by Joseph Hackman.
 
-- Issue #26634: recursive_repr() now sets __qualname__ of wrapper.  Patch by
+- bpo-26634: recursive_repr() now sets __qualname__ of wrapper.  Patch by
   Xiang Zhang.
 
-- Issue #26804: urllib.request will prefer lower_case proxy environment
-  variables over UPPER_CASE or Mixed_Case ones. Patch contributed by Hans-Peter
-  Jansen.
+- bpo-26804: urllib.request will prefer lower_case proxy environment
+  variables over UPPER_CASE or Mixed_Case ones. Patch contributed by Hans-
+  Peter Jansen.
 
-- Issue #26837: assertSequenceEqual() now correctly outputs non-stringified
-  differing items (like bytes in the -b mode).  This affects assertListEqual()
-  and assertTupleEqual().
+- bpo-26837: assertSequenceEqual() now correctly outputs non-stringified
+  differing items (like bytes in the -b mode).  This affects
+  assertListEqual() and assertTupleEqual().
 
-- Issue #26041: Remove "will be removed in Python 3.7" from deprecation
-  messages of platform.dist() and platform.linux_distribution().
-  Patch by Kumaripaba Miyurusara Athukorala.
+- bpo-26041: Remove "will be removed in Python 3.7" from deprecation
+  messages of platform.dist() and platform.linux_distribution(). Patch by
+  Kumaripaba Miyurusara Athukorala.
 
-- Issue #26822: itemgetter, attrgetter and methodcaller objects no longer
+- bpo-26822: itemgetter, attrgetter and methodcaller objects no longer
   silently ignore keyword arguments.
 
-- Issue #26733: Disassembling a class now disassembles class and static methods.
-  Patch by Xiang Zhang.
+- bpo-26733: Disassembling a class now disassembles class and static
+  methods. Patch by Xiang Zhang.
 
-- Issue #26801: Fix error handling in :func:`shutil.get_terminal_size`, catch
-  :exc:`AttributeError` instead of :exc:`NameError`. Patch written by Emanuel
-  Barry.
+- bpo-26801: Fix error handling in :func:`shutil.get_terminal_size`, catch
+  :exc:`AttributeError` instead of :exc:`NameError`. Patch written by
+  Emanuel Barry.
 
-- Issue #24838: tarfile's ustar and gnu formats now correctly calculate name
+- bpo-24838: tarfile's ustar and gnu formats now correctly calculate name
   and link field limits for multibyte character encodings like utf-8.
 
-- [Security] Issue #26657: Fix directory traversal vulnerability with
-  http.server on Windows.  This fixes a regression that was introduced in
-  3.3.4rc1 and 3.4.0rc1.  Based on patch by Philipp Hagemeister.
+Security
+--------
 
-- Issue #26717: Stop encoding Latin-1-ized WSGI paths with UTF-8.  Patch by
+- bpo-26657: Fix directory traversal vulnerability with http.server on
+  Windows.  This fixes a regression that was introduced in 3.3.4rc1 and
+  3.4.0rc1.  Based on patch by Philipp Hagemeister.
+
+Library
+-------
+
+- bpo-26717: Stop encoding Latin-1-ized WSGI paths with UTF-8.  Patch by
   Anthony Sottile.
 
-- Issue #26782: Add STARTUPINFO to subprocess.__all__ on Windows.
+- bpo-26782: Add STARTUPINFO to subprocess.__all__ on Windows.
 
-- Issue #26404: Add context manager to socketserver.  Patch by Aviv Palivoda.
+- bpo-26404: Add context manager to socketserver.  Patch by Aviv Palivoda.
 
-- Issue #26735: Fix :func:`os.urandom` on Solaris 11.3 and newer when reading
+- bpo-26735: Fix :func:`os.urandom` on Solaris 11.3 and newer when reading
   more than 1,024 bytes: call ``getrandom()`` multiple times with a limit of
   1024 bytes per call.
 
-- Issue #26585: Eliminate http.server._quote_html() and use
+- bpo-26585: Eliminate http.server._quote_html() and use
   html.escape(quote=False).  Patch by Xiang Zhang.
 
-- Issue #26685: Raise OSError if closing a socket fails.
+- bpo-26685: Raise OSError if closing a socket fails.
 
-- Issue #16329: Add .webm to mimetypes.types_map.  Patch by Giampaolo Rodola'.
+- bpo-16329: Add .webm to mimetypes.types_map.  Patch by Giampaolo Rodola'.
 
-- Issue #13952: Add .csv to mimetypes.types_map.  Patch by Geoff Wilson.
+- bpo-13952: Add .csv to mimetypes.types_map.  Patch by Geoff Wilson.
 
-- Issue #26587: the site module now allows .pth files to specify files to be
+- bpo-26587: the site module now allows .pth files to specify files to be
   added to sys.path (e.g. zip files).
 
-- Issue #25609: Introduce contextlib.AbstractContextManager and
+- bpo-25609: Introduce contextlib.AbstractContextManager and
   typing.ContextManager.
 
-- Issue #26709: Fixed Y2038 problem in loading binary PLists.
+- bpo-26709: Fixed Y2038 problem in loading binary PLists.
 
-- Issue #23735: Handle terminal resizing with Readline 6.3+ by installing our
+- bpo-23735: Handle terminal resizing with Readline 6.3+ by installing our
   own SIGWINCH handler.  Patch by Eric Price.
 
-- Issue #25951: Change SSLSocket.sendall() to return None, as explicitly
+- bpo-25951: Change SSLSocket.sendall() to return None, as explicitly
   documented for plain socket objects.  Patch by Aviv Palivoda.
 
-- Issue #26586: In http.server, respond with "413 Request header fields too
+- bpo-26586: In http.server, respond with "413 Request header fields too
   large" if there are too many header fields to parse, rather than killing
   the connection and raising an unhandled exception.  Patch by Xiang Zhang.
 
-- Issue #26676: Added missing XMLPullParser to ElementTree.__all__.
+- bpo-26676: Added missing XMLPullParser to ElementTree.__all__.
 
-- Issue #22854: Change BufferedReader.writable() and
-  BufferedWriter.readable() to always return False.
+- bpo-22854: Change BufferedReader.writable() and BufferedWriter.readable()
+  to always return False.
 
-- Issue #26492: Exhausted iterator of array.array now conforms with the behavior
-  of iterators of other mutable sequences: it lefts exhausted even if iterated
-  array is extended.
+- bpo-26492: Exhausted iterator of array.array now conforms with the
+  behavior of iterators of other mutable sequences: it lefts exhausted even
+  if iterated array is extended.
 
-- Issue #26641: doctest.DocFileTest and doctest.testfile() now support
-  packages (module splitted into multiple directories) for the package
-  parameter.
+- bpo-26641: doctest.DocFileTest and doctest.testfile() now support packages
+  (module splitted into multiple directories) for the package parameter.
 
-- Issue #25195: Fix a regression in mock.MagicMock. _Call is a subclass of
+- bpo-25195: Fix a regression in mock.MagicMock. _Call is a subclass of
   tuple (changeset 3603bae63c13 only works for classes) so we need to
   implement __ne__ ourselves.  Patch by Andrew Plummer.
 
-- Issue #26644: Raise ValueError rather than SystemError when a negative
-  length is passed to SSLSocket.recv() or read().
+- bpo-26644: Raise ValueError rather than SystemError when a negative length
+  is passed to SSLSocket.recv() or read().
 
-- Issue #23804: Fix SSL recv(0) and read(0) methods to return zero bytes
+- bpo-23804: Fix SSL recv(0) and read(0) methods to return zero bytes
   instead of up to 1024.
 
-- Issue #26616: Fixed a bug in datetime.astimezone() method.
+- bpo-26616: Fixed a bug in datetime.astimezone() method.
 
-- Issue #26637: The :mod:`importlib` module now emits an :exc:`ImportError`
+- bpo-26637: The :mod:`importlib` module now emits an :exc:`ImportError`
   rather than a :exc:`TypeError` if :func:`__import__` is tried during the
   Python shutdown process but :data:`sys.path` is already cleared (set to
   ``None``).
 
-- Issue #21925: :func:`warnings.formatwarning` now catches exceptions when
+- bpo-21925: :func:`warnings.formatwarning` now catches exceptions when
   calling :func:`linecache.getline` and
   :func:`tracemalloc.get_object_traceback` to be able to log
   :exc:`ResourceWarning` emitted late during the Python shutdown process.
 
-- Issue #23848: On Windows, faulthandler.enable() now also installs an
-  exception handler to dump the traceback of all Python threads on any Windows
-  exception, not only on UNIX signals (SIGSEGV, SIGFPE, SIGABRT).
+- bpo-23848: On Windows, faulthandler.enable() now also installs an
+  exception handler to dump the traceback of all Python threads on any
+  Windows exception, not only on UNIX signals (SIGSEGV, SIGFPE, SIGABRT).
 
-- Issue #26530: Add C functions :c:func:`_PyTraceMalloc_Track` and
+- bpo-26530: Add C functions :c:func:`_PyTraceMalloc_Track` and
   :c:func:`_PyTraceMalloc_Untrack` to track memory blocks using the
-  :mod:`tracemalloc` module. Add :c:func:`_PyTraceMalloc_GetTraceback` to get
-  the traceback of an object.
+  :mod:`tracemalloc` module. Add :c:func:`_PyTraceMalloc_GetTraceback` to
+  get the traceback of an object.
 
-- Issue #26588: The _tracemalloc now supports tracing memory allocations of
+- bpo-26588: The _tracemalloc now supports tracing memory allocations of
   multiple address spaces (domains).
 
-- Issue #24266: Ctrl+C during Readline history search now cancels the search
+- bpo-24266: Ctrl+C during Readline history search now cancels the search
   mode when compiled with Readline 7.
 
-- Issue #26590: Implement a safe finalizer for the _socket.socket type. It now
+- bpo-26590: Implement a safe finalizer for the _socket.socket type. It now
   releases the GIL to close the socket.
 
-- Issue #18787: spwd.getspnam() now raises a PermissionError if the user
+- bpo-18787: spwd.getspnam() now raises a PermissionError if the user
   doesn't have privileges.
 
-- Issue #26560: Avoid potential ValueError in BaseHandler.start_response.
+- bpo-26560: Avoid potential ValueError in BaseHandler.start_response.
   Initial patch by Peter Inglesby.
 
-- Issue #26567: Add a new function :c:func:`PyErr_ResourceWarning` function to
+- bpo-26567: Add a new function :c:func:`PyErr_ResourceWarning` function to
   pass the destroyed object. Add a *source* attribute to
   :class:`warnings.WarningMessage`. Add warnings._showwarnmsg() which uses
   tracemalloc to get the traceback where source object was allocated.
 
-- [Security] Issue #26313: ssl.py _load_windows_store_certs fails if windows
-  cert store is empty. Patch by Baji.
+Security
+--------
+
+- bpo-26313: ssl.py _load_windows_store_certs fails if windows cert store is
+  empty. Patch by Baji.
 
-- Issue #26569: Fix :func:`pyclbr.readmodule` and :func:`pyclbr.readmodule_ex`
+Library
+-------
+
+- bpo-26569: Fix :func:`pyclbr.readmodule` and :func:`pyclbr.readmodule_ex`
   to support importing packages.
 
-- Issue #26499: Account for remaining Content-Length in
-  HTTPResponse.readline() and read1().  Based on patch by Silent Ghost.
-  Also document that HTTPResponse now supports these methods.
+- bpo-26499: Account for remaining Content-Length in HTTPResponse.readline()
+  and read1(). Based on patch by Silent Ghost. Also document that
+  HTTPResponse now supports these methods.
 
-- Issue #25320: Handle sockets in directories unittest discovery is scanning.
+- bpo-25320: Handle sockets in directories unittest discovery is scanning.
   Patch from Victor van den Elzen.
 
-- Issue #16181: cookiejar.http2time() now returns None if year is higher than
+- bpo-16181: cookiejar.http2time() now returns None if year is higher than
   datetime.MAXYEAR.
 
-- Issue #26513: Fixes platform module detection of Windows Server
+- bpo-26513: Fixes platform module detection of Windows Server
 
-- Issue #23718: Fixed parsing time in week 0 before Jan 1.  Original patch by
+- bpo-23718: Fixed parsing time in week 0 before Jan 1.  Original patch by
   Tamás Bence Gedai.
 
-- Issue #26323: Add Mock.assert_called() and Mock.assert_called_once()
-  methods to unittest.mock. Patch written by Amit Saha.
+- bpo-26323: Add Mock.assert_called() and Mock.assert_called_once() methods
+  to unittest.mock. Patch written by Amit Saha.
 
-- Issue #20589: Invoking Path.owner() and Path.group() on Windows now raise
+- bpo-20589: Invoking Path.owner() and Path.group() on Windows now raise
   NotImplementedError instead of ImportError.
 
-- Issue #26177: Fixed the keys() method for Canvas and Scrollbar widgets.
+- bpo-26177: Fixed the keys() method for Canvas and Scrollbar widgets.
 
-- Issue #15068: Got rid of excessive buffering in fileinput.
-  The bufsize parameter is now deprecated and ignored.
+- bpo-15068: Got rid of excessive buffering in fileinput. The bufsize
+  parameter is now deprecated and ignored.
 
-- Issue #19475: Added an optional argument timespec to the datetime
-  isoformat() method to choose the precision of the time component.
+- bpo-19475: Added an optional argument timespec to the datetime isoformat()
+  method to choose the precision of the time component.
 
-- Issue #2202: Fix UnboundLocalError in
-  AbstractDigestAuthHandler.get_algorithm_impls.  Initial patch by Mathieu
+- bpo-2202: Fix UnboundLocalError in
+  AbstractDigestAuthHandler.get_algorithm_impls. Initial patch by Mathieu
   Dupuy.
 
-- Issue #26167: Minimized overhead in copy.copy() and copy.deepcopy().
+- bpo-26167: Minimized overhead in copy.copy() and copy.deepcopy().
   Optimized copying and deepcopying bytearrays, NotImplemented, slices,
   short lists, tuples, dicts, sets.
 
-- Issue #25718: Fixed pickling and copying the accumulate() iterator with
-  total is None.
+- bpo-25718: Fixed pickling and copying the accumulate() iterator with total
+  is None.
 
-- Issue #26475: Fixed debugging output for regular expressions with the (?x)
+- bpo-26475: Fixed debugging output for regular expressions with the (?x)
   flag.
 
-- Issue #26482: Allowed pickling recursive dequeues.
+- bpo-26482: Allowed pickling recursive dequeues.
 
-- Issue #26335: Make mmap.write() return the number of bytes written like
-  other write methods.  Patch by Jakub Stasiak.
+- bpo-26335: Make mmap.write() return the number of bytes written like other
+  write methods.  Patch by Jakub Stasiak.
 
-- Issue #26457: Fixed the subnets() methods in IP network classes for the case
-  when resulting prefix length is equal to maximal prefix length.
-  Based on patch by Xiang Zhang.
+- bpo-26457: Fixed the subnets() methods in IP network classes for the case
+  when resulting prefix length is equal to maximal prefix length. Based on
+  patch by Xiang Zhang.
 
-- Issue #26385: Remove the file if the internal open() call in
-  NamedTemporaryFile() fails.  Patch by Silent Ghost.
+- bpo-26385: Remove the file if the internal open() call in
+  NamedTemporaryFile() fails. Patch by Silent Ghost.
 
-- Issue #26402: Fix XML-RPC client to retry when the server shuts down a
+- bpo-26402: Fix XML-RPC client to retry when the server shuts down a
   persistent connection.  This was a regression related to the new
   http.client.RemoteDisconnected exception in 3.5.0a4.
 
-- Issue #25913: Leading ``<~`` is optional now in base64.a85decode() with
+- bpo-25913: Leading ``<~`` is optional now in base64.a85decode() with
   adobe=True.  Patch by Swati Jaiswal.
 
-- Issue #26186: Remove an invalid type check in importlib.util.LazyLoader.
+- bpo-26186: Remove an invalid type check in importlib.util.LazyLoader.
 
-- Issue #26367: importlib.__import__() raises ImportError like
-  builtins.__import__() when ``level`` is specified but without an accompanying
-  package specified.
+- bpo-26367: importlib.__import__() raises ImportError like
+  builtins.__import__() when ``level`` is specified but without an
+  accompanying package specified.
 
-- Issue #26309: In the "socketserver" module, shut down the request (closing
+- bpo-26309: In the "socketserver" module, shut down the request (closing
   the connected socket) when verify_request() returns false.  Patch by Aviv
   Palivoda.
 
-- Issue #23430: Change the socketserver module to only catch exceptions
-  raised from a request handler that are derived from Exception (instead of
-  BaseException).  Therefore SystemExit and KeyboardInterrupt no longer
+- bpo-23430: Change the socketserver module to only catch exceptions raised
+  from a request handler that are derived from Exception (instead of
+  BaseException). Therefore SystemExit and KeyboardInterrupt no longer
   trigger the handle_error() method, and will now to stop a single-threaded
   server.
 
-- [Security] Issue #25939: On Windows open the cert store readonly in
+Security
+--------
+
+- bpo-25939: On Windows open the cert store readonly in
   ssl.enum_certificates.
 
-- Issue #25995: os.walk() no longer uses FDs proportional to the tree depth.
+Library
+-------
 
-- Issue #25994: Added the close() method and the support of the context manager
+- bpo-25995: os.walk() no longer uses FDs proportional to the tree depth.
+
+- bpo-25994: Added the close() method and the support of the context manager
   protocol for the os.scandir() iterator.
 
-- Issue #23992: multiprocessing: make MapResult not fail-fast upon exception.
+- bpo-23992: multiprocessing: make MapResult not fail-fast upon exception.
 
-- Issue #26243: Support keyword arguments to zlib.compress().  Patch by Aviv
+- bpo-26243: Support keyword arguments to zlib.compress().  Patch by Aviv
   Palivoda.
 
-- Issue #26117: The os.scandir() iterator now closes file descriptor not only
+- bpo-26117: The os.scandir() iterator now closes file descriptor not only
   when the iteration is finished, but when it was failed with error.
 
-- Issue #25949: __dict__ for an OrderedDict instance is now created only when
+- bpo-25949: __dict__ for an OrderedDict instance is now created only when
   needed.
 
-- Issue #25911: Restored support of bytes paths in os.walk() on Windows.
+- bpo-25911: Restored support of bytes paths in os.walk() on Windows.
 
-- Issue #26045: Add UTF-8 suggestion to error message when posting a
-  non-Latin-1 string with http.client.
+- bpo-26045: Add UTF-8 suggestion to error message when posting a non-
+  Latin-1 string with http.client.
 
-- Issue #26039: Added zipfile.ZipInfo.from_file() and zipinfo.ZipInfo.is_dir().
+- bpo-26039: Added zipfile.ZipInfo.from_file() and zipinfo.ZipInfo.is_dir().
   Patch by Thomas Kluyver.
 
-- Issue #12923: Reset FancyURLopener's redirect counter even if there is an
-  exception.  Based on patches by Brian Brazil and Daniel Rocco.
+- bpo-12923: Reset FancyURLopener's redirect counter even if there is an
+  exception. Based on patches by Brian Brazil and Daniel Rocco.
 
-- Issue #25945: Fixed a crash when unpickle the functools.partial object with
-  wrong state.  Fixed a leak in failed functools.partial constructor.
-  "args" and "keywords" attributes of functools.partial have now always types
-  tuple and dict correspondingly.
+- bpo-25945: Fixed a crash when unpickle the functools.partial object with
+  wrong state. Fixed a leak in failed functools.partial constructor. "args"
+  and "keywords" attributes of functools.partial have now always types tuple
+  and dict correspondingly.
 
-- Issue #26202: copy.deepcopy() now correctly copies range() objects with
-  non-atomic attributes.
+- bpo-26202: copy.deepcopy() now correctly copies range() objects with non-
+  atomic attributes.
 
-- Issue #23076: Path.glob() now raises a ValueError if it's called with an
-  invalid pattern.  Patch by Thomas Nyberg.
+- bpo-23076: Path.glob() now raises a ValueError if it's called with an
+  invalid pattern. Patch by Thomas Nyberg.
 
-- Issue #19883: Fixed possible integer overflows in zipimport.
+- bpo-19883: Fixed possible integer overflows in zipimport.
 
-- Issue #26227: On Windows, getnameinfo(), gethostbyaddr() and
+- bpo-26227: On Windows, getnameinfo(), gethostbyaddr() and
   gethostbyname_ex() functions of the socket module now decode the hostname
   from the ANSI code page rather than UTF-8.
 
-- Issue #26099: The site module now writes an error into stderr if
+- bpo-26099: The site module now writes an error into stderr if
   sitecustomize module can be imported but executing the module raise an
   ImportError. Same change for usercustomize.
 
-- Issue #26147: xmlrpc now works with strings not encodable with used
-  non-UTF-8 encoding.
+- bpo-26147: xmlrpc now works with strings not encodable with used non-UTF-8
+  encoding.
 
-- Issue #25935: Garbage collector now breaks reference loops with OrderedDict.
+- bpo-25935: Garbage collector now breaks reference loops with OrderedDict.
 
-- Issue #16620: Fixed AttributeError in msilib.Directory.glob().
+- bpo-16620: Fixed AttributeError in msilib.Directory.glob().
 
-- Issue #26013: Added compatibility with broken protocol 2 pickles created
-  in old Python 3 versions (3.4.3 and lower).
+- bpo-26013: Added compatibility with broken protocol 2 pickles created in
+  old Python 3 versions (3.4.3 and lower).
 
-- Issue #26129: Deprecated accepting non-integers in grp.getgrgid().
+- bpo-26129: Deprecated accepting non-integers in grp.getgrgid().
 
-- Issue #25850: Use cross-compilation by default for 64-bit Windows.
+- bpo-25850: Use cross-compilation by default for 64-bit Windows.
 
-- Issue #25822: Add docstrings to the fields of urllib.parse results.
-  Patch contributed by Swati Jaiswal.
+- bpo-25822: Add docstrings to the fields of urllib.parse results. Patch
+  contributed by Swati Jaiswal.
 
-- Issue #22642: Convert trace module option parsing mechanism to argparse.
+- bpo-22642: Convert trace module option parsing mechanism to argparse.
   Patch contributed by SilentGhost.
 
-- Issue #24705: Fix sysconfig._parse_makefile not expanding ${} vars
-  appearing before $() vars.
+- bpo-24705: Fix sysconfig._parse_makefile not expanding ${} vars appearing
+  before $() vars.
 
-- Issue #26069: Remove the deprecated apis in the trace module.
+- bpo-26069: Remove the deprecated apis in the trace module.
 
-- Issue #22138: Fix mock.patch behavior when patching descriptors. Restore
+- bpo-22138: Fix mock.patch behavior when patching descriptors. Restore
   original values after patching. Patch contributed by Sean McCully.
 
-- Issue #25672: In the ssl module, enable the SSL_MODE_RELEASE_BUFFERS mode
+- bpo-25672: In the ssl module, enable the SSL_MODE_RELEASE_BUFFERS mode
   option if it is safe to do so.
 
-- Issue #26012: Don't traverse into symlinks for ``**`` pattern in
+- bpo-26012: Don't traverse into symlinks for ``**`` pattern in
   pathlib.Path.[r]glob().
 
-- Issue #24120: Ignore PermissionError when traversing a tree with
-  pathlib.Path.[r]glob().  Patch by Ulrich Petri.
+- bpo-24120: Ignore PermissionError when traversing a tree with
+  pathlib.Path.[r]glob(). Patch by Ulrich Petri.
 
-- Issue #21815: Accept ] characters in the data portion of imap responses,
-  in order to handle the flags with square brackets accepted and produced
-  by servers such as gmail.
+- bpo-21815: Accept ] characters in the data portion of imap responses, in
+  order to handle the flags with square brackets accepted and produced by
+  servers such as gmail.
 
-- Issue #25447: fileinput now uses sys.stdin as-is if it does not have a
-  buffer attribute (restores backward compatibility).
+- bpo-25447: fileinput now uses sys.stdin as-is if it does not have a buffer
+  attribute (restores backward compatibility).
 
-- Issue #25971: Optimized creating Fractions from floats by 2 times and from
+- bpo-25971: Optimized creating Fractions from floats by 2 times and from
   Decimals by 3 times.
 
-- Issue #25802: Document as deprecated the remaining implementations of
+- bpo-25802: Document as deprecated the remaining implementations of
   importlib.abc.Loader.load_module().
 
-- Issue #25928: Add Decimal.as_integer_ratio().
+- bpo-25928: Add Decimal.as_integer_ratio().
 
-- Issue #25447: Copying the lru_cache() wrapper object now always works,
-  independently from the type of the wrapped object (by returning the original
-  object unchanged).
+- bpo-25447: Copying the lru_cache() wrapper object now always works,
+  independently from the type of the wrapped object (by returning the
+  original object unchanged).
 
-- Issue #25768: Have the functions in compileall return booleans instead of
+- bpo-25768: Have the functions in compileall return booleans instead of
   ints and add proper documentation and tests for the return values.
 
-- Issue #24103: Fixed possible use after free in ElementTree.XMLPullParser.
+- bpo-24103: Fixed possible use after free in ElementTree.XMLPullParser.
 
-- Issue #25860: os.fwalk() no longer skips remaining directories when error
-  occurs.  Original patch by Samson Lee.
+- bpo-25860: os.fwalk() no longer skips remaining directories when error
+  occurs. Original patch by Samson Lee.
 
-- Issue #25914: Fixed and simplified OrderedDict.__sizeof__.
+- bpo-25914: Fixed and simplified OrderedDict.__sizeof__.
 
-- Issue #25869: Optimized deepcopying ElementTree; it is now 20 times faster.
+- bpo-25869: Optimized deepcopying ElementTree; it is now 20 times faster.
 
-- Issue #25873: Optimized iterating ElementTree.  Iterating elements
-  Element.iter() is now 40% faster, iterating text Element.itertext()
-  is now up to 2.5 times faster.
+- bpo-25873: Optimized iterating ElementTree.  Iterating elements
+  Element.iter() is now 40% faster, iterating text Element.itertext() is now
+  up to 2.5 times faster.
 
-- Issue #25902: Fixed various refcount issues in ElementTree iteration.
+- bpo-25902: Fixed various refcount issues in ElementTree iteration.
 
-- Issue #22227: The TarFile iterator is reimplemented using generator.
-  This implementation is simpler that using class.
+- bpo-22227: The TarFile iterator is reimplemented using generator. This
+  implementation is simpler that using class.
 
-- Issue #25638: Optimized ElementTree.iterparse(); it is now 2x faster.
+- bpo-25638: Optimized ElementTree.iterparse(); it is now 2x faster.
   Optimized ElementTree parsing; it is now 10% faster.
 
-- Issue #25761: Improved detecting errors in broken pickle data.
+- bpo-25761: Improved detecting errors in broken pickle data.
 
-- Issue #25717: Restore the previous behaviour of tolerating most fstat()
+- bpo-25717: Restore the previous behaviour of tolerating most fstat()
   errors when opening files.  This was a regression in 3.5a1, and stopped
   anonymous temporary files from working in special cases.
 
-- Issue #24903: Fix regression in number of arguments compileall accepts when
+- bpo-24903: Fix regression in number of arguments compileall accepts when
   '-d' is specified.  The check on the number of arguments has been dropped
   completely as it never worked correctly anyway.
 
-- Issue #25764: In the subprocess module, preserve any exception caused by
+- bpo-25764: In the subprocess module, preserve any exception caused by
   fork() failure when preexec_fn is used.
 
-- Issue #25771: Tweak the exception message for importlib.util.resolve_name()
+- bpo-25771: Tweak the exception message for importlib.util.resolve_name()
   when 'package' isn't specified but necessary.
 
-- Issue #6478: _strptime's regexp cache now is reset after changing timezone
+- bpo-6478: _strptime's regexp cache now is reset after changing timezone
   with time.tzset().
 
-- Issue #14285: When executing a package with the "python -m package" option,
+- bpo-14285: When executing a package with the "python -m package" option,
   and package initialization fails, a proper traceback is now reported.  The
-  "runpy" module now lets exceptions from package initialization pass back to
-  the caller, rather than raising ImportError.
+  "runpy" module now lets exceptions from package initialization pass back
+  to the caller, rather than raising ImportError.
 
-- Issue #19771: Also in runpy and the "-m" option, omit the irrelevant
-  message ". . . is a package and cannot be directly executed" if the package
-  could not even be initialized (e.g. due to a bad ``*.pyc`` file).
+- bpo-19771: Also in runpy and the "-m" option, omit the irrelevant message
+  ". . . is a package and cannot be directly executed" if the package could
+  not even be initialized (e.g. due to a bad ``*.pyc`` file).
 
-- Issue #25177: Fixed problem with the mean of very small and very large
+- bpo-25177: Fixed problem with the mean of very small and very large
   numbers. As a side effect, statistics.mean and statistics.variance should
   be significantly faster.
 
-- Issue #25718: Fixed copying object with state with boolean value is false.
+- bpo-25718: Fixed copying object with state with boolean value is false.
 
-- Issue #10131: Fixed deep copying of minidom documents.  Based on patch
-  by Marian Ganisin.
+- bpo-10131: Fixed deep copying of minidom documents.  Based on patch by
+  Marian Ganisin.
 
-- Issue #7990: dir() on ElementTree.Element now lists properties: "tag",
+- bpo-7990: dir() on ElementTree.Element now lists properties: "tag",
   "text", "tail" and "attrib".  Original patch by Santoso Wijaya.
 
-- Issue #25725: Fixed a reference leak in pickle.loads() when unpickling
+- bpo-25725: Fixed a reference leak in pickle.loads() when unpickling
   invalid data including tuple instructions.
 
-- Issue #25663: In the Readline completer, avoid listing duplicate global
+- bpo-25663: In the Readline completer, avoid listing duplicate global
   names, and search the global namespace before searching builtins.
 
-- Issue #25688: Fixed file leak in ElementTree.iterparse() raising an error.
+- bpo-25688: Fixed file leak in ElementTree.iterparse() raising an error.
 
-- Issue #23914: Fixed SystemError raised by unpickler on broken pickle data.
+- bpo-23914: Fixed SystemError raised by unpickler on broken pickle data.
 
-- Issue #25691: Fixed crash on deleting ElementTree.Element attributes.
+- bpo-25691: Fixed crash on deleting ElementTree.Element attributes.
 
-- Issue #25624: ZipFile now always writes a ZIP_STORED header for directory
+- bpo-25624: ZipFile now always writes a ZIP_STORED header for directory
   entries.  Patch by Dingyuan Wang.
 
-- Issue #25626: Change three zlib functions to accept sizes that fit in
+- bpo-25626: Change three zlib functions to accept sizes that fit in
   Py_ssize_t, but internally cap those sizes to UINT_MAX.  This resolves a
-  regression in 3.5 where GzipFile.read() failed to read chunks larger than 2
-  or 4 GiB.  The change affects the zlib.Decompress.decompress() max_length
-  parameter, the zlib.decompress() bufsize parameter, and the
+  regression in 3.5 where GzipFile.read() failed to read chunks larger than
+  2 or 4 GiB.  The change affects the zlib.Decompress.decompress()
+  max_length parameter, the zlib.decompress() bufsize parameter, and the
   zlib.Decompress.flush() length parameter.
 
-- Issue #25583: Avoid incorrect errors raised by os.makedirs(exist_ok=True)
+- bpo-25583: Avoid incorrect errors raised by os.makedirs(exist_ok=True)
   when the OS gives priority to errors such as EACCES over EEXIST.
 
-- Issue #25593: Change semantics of EventLoop.stop() in asyncio.
+- bpo-25593: Change semantics of EventLoop.stop() in asyncio.
 
-- Issue #6973: When we know a subprocess.Popen process has died, do
-  not allow the send_signal(), terminate(), or kill() methods to do
-  anything as they could potentially signal a different process.
+- bpo-6973: When we know a subprocess.Popen process has died, do not allow
+  the send_signal(), terminate(), or kill() methods to do anything as they
+  could potentially signal a different process.
 
-- Issue #23883: Added missing APIs to __all__ to match the documented APIs
-  for the following modules: calendar, csv, enum, fileinput, ftplib, logging,
+- bpo-23883: Added missing APIs to __all__ to match the documented APIs for
+  the following modules: calendar, csv, enum, fileinput, ftplib, logging,
   optparse, tarfile, threading and wave.  Also added a
-  test.support.check__all__() helper.  Patches by Jacek Kołodziej, Mauro
-  S. M. Rodrigues and Joel Taddei.
+  test.support.check__all__() helper. Patches by Jacek Kołodziej, Mauro S.
+  M. Rodrigues and Joel Taddei.
 
-- Issue #25590: In the Readline completer, only call getattr() once per
+- bpo-25590: In the Readline completer, only call getattr() once per
   attribute.  Also complete names of attributes such as properties and slots
   which are listed by dir() but not yet created on an instance.
 
-- Issue #25498: Fix a crash when garbage-collecting ctypes objects created
-  by wrapping a memoryview.  This was a regression made in 3.5a1.  Based
-  on patch by Eryksun.
+- bpo-25498: Fix a crash when garbage-collecting ctypes objects created by
+  wrapping a memoryview.  This was a regression made in 3.5a1.  Based on
+  patch by Eryksun.
 
-- Issue #25584: Added "escape" to the __all__ list in the glob module.
+- bpo-25584: Added "escape" to the __all__ list in the glob module.
 
-- Issue #25584: Fixed recursive glob() with patterns starting with ``**``.
+- bpo-25584: Fixed recursive glob() with patterns starting with ``**``.
 
-- Issue #25446: Fix regression in smtplib's AUTH LOGIN support.
+- bpo-25446: Fix regression in smtplib's AUTH LOGIN support.
 
-- Issue #18010: Fix the pydoc web server's module search function to handle
+- bpo-18010: Fix the pydoc web server's module search function to handle
   exceptions from importing packages.
 
-- Issue #25554: Got rid of circular references in regular expression parsing.
+- bpo-25554: Got rid of circular references in regular expression parsing.
 
-- Issue #18973: Command-line interface of the calendar module now uses argparse
+- bpo-18973: Command-line interface of the calendar module now uses argparse
   instead of optparse.
 
-- Issue #25510: fileinput.FileInput.readline() now returns b'' instead of ''
-  at the end if the FileInput was opened with binary mode.
-  Patch by Ryosuke Ito.
+- bpo-25510: fileinput.FileInput.readline() now returns b'' instead of '' at
+  the end if the FileInput was opened with binary mode. Patch by Ryosuke
+  Ito.
 
-- Issue #25503: Fixed inspect.getdoc() for inherited docstrings of properties.
+- bpo-25503: Fixed inspect.getdoc() for inherited docstrings of properties.
   Original patch by John Mark Vandenberg.
 
-- Issue #25515: Always use os.urandom as a source of randomness in uuid.uuid4.
+- bpo-25515: Always use os.urandom as a source of randomness in uuid.uuid4.
 
-- Issue #21827: Fixed textwrap.dedent() for the case when largest common
-  whitespace is a substring of smallest leading whitespace.
-  Based on patch by Robert Li.
+- bpo-21827: Fixed textwrap.dedent() for the case when largest common
+  whitespace is a substring of smallest leading whitespace. Based on patch
+  by Robert Li.
 
-- Issue #25447: The lru_cache() wrapper objects now can be copied and pickled
+- bpo-25447: The lru_cache() wrapper objects now can be copied and pickled
   (by returning the original object unchanged).
 
-- Issue #25390: typing: Don't crash on Union[str, Pattern].
+- bpo-25390: typing: Don't crash on Union[str, Pattern].
 
-- Issue #25441: asyncio: Raise error from drain() when socket is closed.
+- bpo-25441: asyncio: Raise error from drain() when socket is closed.
 
-- Issue #25410: Cleaned up and fixed minor bugs in C implementation of
+- bpo-25410: Cleaned up and fixed minor bugs in C implementation of
   OrderedDict.
 
-- Issue #25411: Improved Unicode support in SMTPHandler through better use of
+- bpo-25411: Improved Unicode support in SMTPHandler through better use of
   the email package. Thanks to user simon04 for the patch.
 
-- Move the imp module from a PendingDeprecationWarning to DeprecationWarning.
+- Move the imp module from a PendingDeprecationWarning to
+  DeprecationWarning.
 
-- Issue #25407: Remove mentions of the formatter module being removed in
-  Python 3.6.
+- bpo-25407: Remove mentions of the formatter module being removed in Python
+  3.6.
 
-- Issue #25406: Fixed a bug in C implementation of OrderedDict.move_to_end()
+- bpo-25406: Fixed a bug in C implementation of OrderedDict.move_to_end()
   that caused segmentation fault or hang in iterating after moving several
   items to the start of ordered dict.
 
-- Issue #25382: pickletools.dis() now outputs implicit memo index for the
+- bpo-25382: pickletools.dis() now outputs implicit memo index for the
   MEMOIZE opcode.
 
-- Issue #25357: Add an optional newline paramer to binascii.b2a_base64().
+- bpo-25357: Add an optional newline paramer to binascii.b2a_base64().
   base64.b64encode() uses it to avoid a memory copy.
 
-- Issue #24164: Objects that need calling ``__new__`` with keyword arguments,
+- bpo-24164: Objects that need calling ``__new__`` with keyword arguments,
   can now be pickled using pickle protocols older than protocol version 4.
 
-- Issue #25364: zipfile now works in threads disabled builds.
+- bpo-25364: zipfile now works in threads disabled builds.
 
-- Issue #25328: smtpd's SMTPChannel now correctly raises a ValueError if both
+- bpo-25328: smtpd's SMTPChannel now correctly raises a ValueError if both
   decode_data and enable_SMTPUTF8 are set to true.
 
-- Issue #16099: RobotFileParser now supports Crawl-delay and Request-rate
+- bpo-16099: RobotFileParser now supports Crawl-delay and Request-rate
   extensions.  Patch by Nikolay Bogoychev.
 
-- Issue #25316: distutils raises OSError instead of DistutilsPlatformError
-  when MSVC is not installed.
+- bpo-25316: distutils raises OSError instead of DistutilsPlatformError when
+  MSVC is not installed.
 
-- Issue #25380: Fixed protocol for the STACK_GLOBAL opcode in
+- bpo-25380: Fixed protocol for the STACK_GLOBAL opcode in
   pickletools.opcodes.
 
-- Issue #23972: Updates asyncio datagram create method allowing reuseport
-  and reuseaddr socket options to be set prior to binding the socket.
-  Mirroring the existing asyncio create_server method the reuseaddr option
-  for datagram sockets defaults to True if the O/S is 'posix' (except if the
+- bpo-23972: Updates asyncio datagram create method allowing reuseport and
+  reuseaddr socket options to be set prior to binding the socket. Mirroring
+  the existing asyncio create_server method the reuseaddr option for
+  datagram sockets defaults to True if the O/S is 'posix' (except if the
   platform is Cygwin). Patch by Chris Laws.
 
-- Issue #25304: Add asyncio.run_coroutine_threadsafe().  This lets you
-  submit a coroutine to a loop from another thread, returning a
+- bpo-25304: Add asyncio.run_coroutine_threadsafe().  This lets you submit a
+  coroutine to a loop from another thread, returning a
   concurrent.futures.Future.  By Vincent Michel.
 
-- Issue #25232: Fix CGIRequestHandler to split the query from the URL at the
+- bpo-25232: Fix CGIRequestHandler to split the query from the URL at the
   first question mark (?) rather than the last. Patch from Xiang Zhang.
 
-- Issue #24657: Prevent CGIRequestHandler from collapsing slashes in the
-  query part of the URL as if it were a path. Patch from Xiang Zhang.
+- bpo-24657: Prevent CGIRequestHandler from collapsing slashes in the query
+  part of the URL as if it were a path. Patch from Xiang Zhang.
 
-- Issue #25287: Don't add crypt.METHOD_CRYPT to crypt.methods if it's not
-  supported. Check if it is supported, it may not be supported on OpenBSD for
-  example.
+- bpo-25287: Don't add crypt.METHOD_CRYPT to crypt.methods if it's not
+  supported. Check if it is supported, it may not be supported on OpenBSD
+  for example.
 
-- Issue #23600: Default implementation of tzinfo.fromutc() was returning
-  wrong results in some cases.
+- bpo-23600: Default implementation of tzinfo.fromutc() was returning wrong
+  results in some cases.
 
-- Issue #25203: Failed readline.set_completer_delims() no longer left the
+- bpo-25203: Failed readline.set_completer_delims() no longer left the
   module in inconsistent state.
 
-- Issue #25011: rlcompleter now omits private and special attribute names unless
-  the prefix starts with underscores.
+- bpo-25011: rlcompleter now omits private and special attribute names
+  unless the prefix starts with underscores.
 
-- Issue #25209: rlcompleter now can add a space or a colon after completed
+- bpo-25209: rlcompleter now can add a space or a colon after completed
   keyword.
 
-- Issue #22241: timezone.utc name is now plain 'UTC', not 'UTC-00:00'.
+- bpo-22241: timezone.utc name is now plain 'UTC', not 'UTC-00:00'.
 
-- Issue #23517: fromtimestamp() and utcfromtimestamp() methods of
+- bpo-23517: fromtimestamp() and utcfromtimestamp() methods of
   datetime.datetime now round microseconds to nearest with ties going to
-  nearest even integer (ROUND_HALF_EVEN), as round(float), instead of rounding
-  towards -Infinity (ROUND_FLOOR).
+  nearest even integer (ROUND_HALF_EVEN), as round(float), instead of
+  rounding towards -Infinity (ROUND_FLOOR).
 
-- Issue #23552: Timeit now warns when there is substantial (4x) variance
+- bpo-23552: Timeit now warns when there is substantial (4x) variance
   between best and worst times. Patch from Serhiy Storchaka.
 
-- Issue #24633: site-packages/README -> README.txt.
+- bpo-24633: site-packages/README -> README.txt.
 
-- Issue #24879: help() and pydoc can now list named tuple fields in the
-  order they were defined rather than alphabetically.  The ordering is
-  determined by the _fields attribute if present.
+- bpo-24879: help() and pydoc can now list named tuple fields in the order
+  they were defined rather than alphabetically.  The ordering is determined
+  by the _fields attribute if present.
 
-- Issue #24874: Improve speed of itertools.cycle() and make its
-  pickle more compact.
+- bpo-24874: Improve speed of itertools.cycle() and make its pickle more
+  compact.
 
 - Fix crash in itertools.cycle.__setstate__() when the first argument wasn't
   a list.
 
-- Issue #20059: urllib.parse raises ValueError on all invalid ports.
-  Patch by Martin Panter.
+- bpo-20059: urllib.parse raises ValueError on all invalid ports. Patch by
+  Martin Panter.
 
-- Issue #24360: Improve __repr__ of argparse.Namespace() for invalid
+- bpo-24360: Improve __repr__ of argparse.Namespace() for invalid
   identifiers.  Patch by Matthias Bussonnier.
 
-- Issue #23426: run_setup was broken in distutils.
-  Patch from Alexander Belopolsky.
+- bpo-23426: run_setup was broken in distutils. Patch from Alexander
+  Belopolsky.
 
-- Issue #13938: 2to3 converts StringTypes to a tuple. Patch from Mark Hammond.
+- bpo-13938: 2to3 converts StringTypes to a tuple. Patch from Mark Hammond.
 
-- Issue #2091: open() accepted a 'U' mode string containing '+', but 'U' can
+- bpo-2091: open() accepted a 'U' mode string containing '+', but 'U' can
   only be used with 'r'. Patch from Jeff Balogh and John O'Connor.
 
-- Issue #8585: improved tests for zipimporter2. Patch from Mark Lawrence.
+- bpo-8585: improved tests for zipimporter2. Patch from Mark Lawrence.
 
-- Issue #18622: unittest.mock.mock_open().reset_mock would recurse infinitely.
+- bpo-18622: unittest.mock.mock_open().reset_mock would recurse infinitely.
   Patch from Nicola Palumbo and Laurent De Buyst.
 
-- Issue #24426: Fast searching optimization in regular expressions now works
-  for patterns that starts with capturing groups.  Fast searching optimization
-  now can't be disabled at compile time.
+- bpo-24426: Fast searching optimization in regular expressions now works
+  for patterns that starts with capturing groups.  Fast searching
+  optimization now can't be disabled at compile time.
 
-- Issue #23661: unittest.mock side_effects can now be exceptions again. This
+- bpo-23661: unittest.mock side_effects can now be exceptions again. This
   was a regression vs Python 3.4. Patch from Ignacio Rossi
 
-- Issue #13248: Remove deprecated inspect.getmoduleinfo function.
+- bpo-13248: Remove deprecated inspect.getmoduleinfo function.
 
-- Issue #25578: Fix (another) memory leak in SSLSocket.getpeercer().
+- bpo-25578: Fix (another) memory leak in SSLSocket.getpeercer().
 
-- Issue #25530: Disable the vulnerable SSLv3 protocol by default when creating
+- bpo-25530: Disable the vulnerable SSLv3 protocol by default when creating
   ssl.SSLContext.
 
-- Issue #25569: Fix memory leak in SSLSocket.getpeercert().
+- bpo-25569: Fix memory leak in SSLSocket.getpeercert().
 
-- Issue #25471: Sockets returned from accept() shouldn't appear to be
+- bpo-25471: Sockets returned from accept() shouldn't appear to be
   nonblocking.
 
-- Issue #25319: When threading.Event is reinitialized, the underlying condition
+- bpo-25319: When threading.Event is reinitialized, the underlying condition
   should use a regular lock rather than a recursive lock.
 
-- Skip getaddrinfo if host is already resolved.
-  Patch by A. Jesse Jiryu Davis.
+- Skip getaddrinfo if host is already resolved. Patch by A. Jesse Jiryu
+  Davis.
 
-- Issue #26050: Add asyncio.StreamReader.readuntil() method.
-  Patch by Марк Коренберг.
+- bpo-26050: Add asyncio.StreamReader.readuntil() method. Patch by Марк
+  Коренберг.
 
-- Issue #25924: Avoid unnecessary serialization of getaddrinfo(3) calls on
-  OS X versions 10.5 or higher.  Original patch by A. Jesse Jiryu Davis.
+- bpo-25924: Avoid unnecessary serialization of getaddrinfo(3) calls on OS X
+  versions 10.5 or higher.  Original patch by A. Jesse Jiryu Davis.
 
-- Issue #26406: Avoid unnecessary serialization of getaddrinfo(3) calls on
+- bpo-26406: Avoid unnecessary serialization of getaddrinfo(3) calls on
   current versions of OpenBSD and NetBSD.  Patch by A. Jesse Jiryu Davis.
 
-- Issue #26848: Fix asyncio/subprocess.communicate() to handle empty input.
+- bpo-26848: Fix asyncio/subprocess.communicate() to handle empty input.
   Patch by Jack O'Connor.
 
-- Issue #27040: Add loop.get_exception_handler method
+- bpo-27040: Add loop.get_exception_handler method
 
-- Issue #27041: asyncio: Add loop.create_future method
+- bpo-27041: asyncio: Add loop.create_future method
 
 IDLE
 ----
 
-- Issue #20640: Add tests for idlelib.configHelpSourceEdit.
-  Patch by Saimadhav Heblikar.
+- bpo-20640: Add tests for idlelib.configHelpSourceEdit. Patch by Saimadhav
+  Heblikar.
 
-- In the 'IDLE-console differences' section of the IDLE doc, clarify
-  how running with IDLE affects sys.modules and the standard streams.
+- In the 'IDLE-console differences' section of the IDLE doc, clarify how
+  running with IDLE affects sys.modules and the standard streams.
 
-- Issue #25507: fix incorrect change in IOBinding that prevented printing.
+- bpo-25507: fix incorrect change in IOBinding that prevented printing.
   Augment IOBinding htest to include all major IOBinding functions.
 
-- Issue #25905: Revert unwanted conversion of ' to ’ RIGHT SINGLE QUOTATION
-  MARK in README.txt and open this and NEWS.txt with 'ascii'.
-  Re-encode CREDITS.txt to utf-8 and open it with 'utf-8'.
+- bpo-25905: Revert unwanted conversion of ' to ’ RIGHT SINGLE QUOTATION
+  MARK in README.txt and open this and NEWS.txt with 'ascii'. Re-encode
+  CREDITS.txt to utf-8 and open it with 'utf-8'.
 
-- Issue #15348: Stop the debugger engine (normally in a user process)
-  before closing the debugger window (running in the IDLE process).
-  This prevents the RuntimeErrors that were being caught and ignored.
+- bpo-15348: Stop the debugger engine (normally in a user process) before
+  closing the debugger window (running in the IDLE process). This prevents
+  the RuntimeErrors that were being caught and ignored.
 
-- Issue #24455: Prevent IDLE from hanging when a) closing the shell while the
+- bpo-24455: Prevent IDLE from hanging when a) closing the shell while the
   debugger is active (15347); b) closing the debugger with the [X] button
-  (15348); and c) activating the debugger when already active (24455).
-  The patch by Mark Roseman does this by making two changes.
-  1. Suspend and resume the gui.interaction method with the tcl vwait
-  mechanism intended for this purpose (instead of root.mainloop & .quit).
-  2. In gui.run, allow any existing interaction to terminate first.
+  (15348); and c) activating the debugger when already active (24455). The
+  patch by Mark Roseman does this by making two changes. 1. Suspend and
+  resume the gui.interaction method with the tcl vwait mechanism intended
+  for this purpose (instead of root.mainloop & .quit). 2. In gui.run, allow
+  any existing interaction to terminate first.
 
 - Change 'The program' to 'Your program' in an IDLE 'kill program?' message
   to make it clearer that the program referred to is the currently running
   user program, not IDLE itself.
 
-- Issue #24750: Improve the appearance of the IDLE editor window status bar.
+- bpo-24750: Improve the appearance of the IDLE editor window status bar.
   Patch by Mark Roseman.
 
-- Issue #25313: Change the handling of new built-in text color themes to better
+- bpo-25313: Change the handling of new built-in text color themes to better
   address the compatibility problem introduced by the addition of IDLE Dark.
   Consistently use the revised idleConf.CurrentTheme everywhere in idlelib.
 
-- Issue #24782: Extension configuration is now a tab in the IDLE Preferences
+- bpo-24782: Extension configuration is now a tab in the IDLE Preferences
   dialog rather than a separate dialog.  The former tabs are now a sorted
   list.  Patch by Mark Roseman.
 
-- Issue #22726: Re-activate the config dialog help button with some content
+- bpo-22726: Re-activate the config dialog help button with some content
   about the other buttons and the new IDLE Dark theme.
 
-- Issue #24820: IDLE now has an 'IDLE Dark' built-in text color theme.
-  It is more or less IDLE Classic inverted, with a cobalt blue background.
-  Strings, comments, keywords, ... are still green, red, orange, ... .
-  To use it with IDLEs released before November 2015, hit the
-  'Save as New Custom Theme' button and enter a new name,
-  such as 'Custom Dark'.  The custom theme will work with any IDLE
-  release, and can be modified.
+- bpo-24820: IDLE now has an 'IDLE Dark' built-in text color theme. It is
+  more or less IDLE Classic inverted, with a cobalt blue background.
+  Strings, comments, keywords, ... are still green, red, orange, ... . To
+  use it with IDLEs released before November 2015, hit the 'Save as New
+  Custom Theme' button and enter a new name, such as 'Custom Dark'.  The
+  custom theme will work with any IDLE release, and can be modified.
 
-- Issue #25224: README.txt is now an idlelib index for IDLE developers and
-  curious users.  The previous user content is now in the IDLE doc chapter.
+- bpo-25224: README.txt is now an idlelib index for IDLE developers and
+  curious users. The previous user content is now in the IDLE doc chapter.
   'IDLE' now means 'Integrated Development and Learning Environment'.
 
-- Issue #24820: Users can now set breakpoint colors in
-  Settings -> Custom Highlighting.  Original patch by Mark Roseman.
+- bpo-24820: Users can now set breakpoint colors in Settings -> Custom
+  Highlighting. Original patch by Mark Roseman.
 
-- Issue #24972: Inactive selection background now matches active selection
+- bpo-24972: Inactive selection background now matches active selection
   background, as configured by users, on all systems.  Found items are now
   always highlighted on Windows.  Initial patch by Mark Roseman.
 
-- Issue #24570: Idle: make calltip and completion boxes appear on Macs
-  affected by a tk regression.  Initial patch by Mark Roseman.
+- bpo-24570: Idle: make calltip and completion boxes appear on Macs affected
+  by a tk regression.  Initial patch by Mark Roseman.
 
-- Issue #24988: Idle ScrolledList context menus (used in debugger)
-  now work on Mac Aqua.  Patch by Mark Roseman.
+- bpo-24988: Idle ScrolledList context menus (used in debugger) now work on
+  Mac Aqua. Patch by Mark Roseman.
 
-- Issue #24801: Make right-click for context menu work on Mac Aqua.
-  Patch by Mark Roseman.
+- bpo-24801: Make right-click for context menu work on Mac Aqua. Patch by
+  Mark Roseman.
 
-- Issue #25173: Associate tkinter messageboxes with a specific widget.
-  For Mac OSX, make them a 'sheet'.  Patch by Mark Roseman.
+- bpo-25173: Associate tkinter messageboxes with a specific widget. For Mac
+  OSX, make them a 'sheet'.  Patch by Mark Roseman.
 
-- Issue #25198: Enhance the initial html viewer now used for Idle Help.
-  * Properly indent fixed-pitch text (patch by Mark Roseman).
-  * Give code snippet a very Sphinx-like light blueish-gray background.
-  * Re-use initial width and height set by users for shell and editor.
-  * When the Table of Contents (TOC) menu is used, put the section header
-  at the top of the screen.
+- bpo-25198: Enhance the initial html viewer now used for Idle Help. *
+  Properly indent fixed-pitch text (patch by Mark Roseman). * Give code
+  snippet a very Sphinx- like light blueish-gray background. * Re-use
+  initial width and height set by users for shell and editor. * When the
+  Table of Contents (TOC) menu is used, put the section header at the top of
+  the screen.
 
-- Issue #25225: Condense and rewrite Idle doc section on text colors.
+- bpo-25225: Condense and rewrite Idle doc section on text colors.
 
-- Issue #21995: Explain some differences between IDLE and console Python.
+- bpo-21995: Explain some differences between IDLE and console Python.
 
-- Issue #22820: Explain need for *print* when running file from Idle editor.
+- bpo-22820: Explain need for *print* when running file from Idle editor.
 
-- Issue #25224: Doc: augment Idle feature list and no-subprocess section.
+- bpo-25224: Doc: augment Idle feature list and no-subprocess section.
 
-- Issue #25219: Update doc for Idle command line options.
-  Some were missing and notes were not correct.
+- bpo-25219: Update doc for Idle command line options. Some were missing and
+  notes were not correct.
 
-- Issue #24861: Most of idlelib is private and subject to change.
-  Use idleib.idle.* to start Idle. See idlelib.__init__.__doc__.
+- bpo-24861: Most of idlelib is private and subject to change. Use
+  idleib.idle.* to start Idle. See idlelib.__init__.__doc__.
 
-- Issue #25199: Idle: add synchronization comments for future maintainers.
+- bpo-25199: Idle: add synchronization comments for future maintainers.
 
-- Issue #16893: Replace help.txt with help.html for Idle doc display.
-  The new idlelib/help.html is rstripped Doc/build/html/library/idle.html.
-  It looks better than help.txt and will better document Idle as released.
-  The tkinter html viewer that works for this file was written by Rose Roseman.
-  The now unused EditorWindow.HelpDialog class and helt.txt file are deprecated.
+- bpo-16893: Replace help.txt with help.html for Idle doc display. The new
+  idlelib/help.html is rstripped Doc/build/html/library/idle.html. It looks
+  better than help.txt and will better document Idle as released. The
+  tkinter html viewer that works for this file was written by Rose Roseman.
+  The now unused EditorWindow.HelpDialog class and helt.txt file are
+  deprecated.
 
-- Issue #24199: Deprecate unused idlelib.idlever with possible removal in 3.6.
+- bpo-24199: Deprecate unused idlelib.idlever with possible removal in 3.6.
 
-- Issue #24790: Remove extraneous code (which also create 2 & 3 conflicts).
+- bpo-24790: Remove extraneous code (which also create 2 & 3 conflicts).
 
 Documentation
 -------------
 
-- Issue #26736: Used HTTPS for external links in the documentation if possible.
+- bpo-26736: Used HTTPS for external links in the documentation if possible.
 
-- Issue #6953: Rework the Readline module documentation to group related
+- bpo-6953: Rework the Readline module documentation to group related
   functions together, and add more details such as what underlying Readline
   functions and variables are accessed.
 
-- Issue #23606: Adds note to ctypes documentation regarding cdll.msvcrt.
+- bpo-23606: Adds note to ctypes documentation regarding cdll.msvcrt.
 
-- Issue #24952: Clarify the default size argument of stack_size() in
-  the "threading" and "_thread" modules. Patch from Mattip.
+- bpo-24952: Clarify the default size argument of stack_size() in the
+  "threading" and "_thread" modules. Patch from Mattip.
 
-- Issue #26014: Update 3.x packaging documentation:
-  * "See also" links to the new docs are now provided in the legacy pages
-  * links to setuptools documentation have been updated
+- bpo-26014: Update 3.x packaging documentation: * "See also" links to the
+  new docs are now provided in the legacy pages * links to setuptools
+  documentation have been updated
 
 Tests
 -----
 
-- Issue #21916: Added tests for the turtle module.  Patch by ingrid,
-  Gregory Loyse and Jelle Zijlstra.
+- bpo-21916: Added tests for the turtle module.  Patch by ingrid, Gregory
+  Loyse and Jelle Zijlstra.
 
-- Issue #26295: When using "python3 -m test --testdir=TESTDIR", regrtest
+- bpo-26295: When using "python3 -m test --testdir=TESTDIR", regrtest
   doesn't add "test." prefix to test module names.
 
-- Issue #26523: The multiprocessing thread pool (multiprocessing.dummy.Pool)
+- bpo-26523: The multiprocessing thread pool (multiprocessing.dummy.Pool)
   was untested.
 
-- Issue #26015: Added new tests for pickling iterators of mutable sequences.
+- bpo-26015: Added new tests for pickling iterators of mutable sequences.
 
-- Issue #26325: Added test.support.check_no_resource_warning() to check that
-  no ResourceWarning is emitted.
+- bpo-26325: Added test.support.check_no_resource_warning() to check that no
+  ResourceWarning is emitted.
 
-- Issue #25940: Changed test_ssl to use its internal local server more.  This
+- bpo-25940: Changed test_ssl to use its internal local server more.  This
   avoids relying on svn.python.org, which recently changed root certificate.
 
-- Issue #25616: Tests for OrderedDict are extracted from test_collections
-  into separate file test_ordered_dict.
+- bpo-25616: Tests for OrderedDict are extracted from test_collections into
+  separate file test_ordered_dict.
 
-- Issue #25449: Added tests for OrderedDict subclasses.
+- bpo-25449: Added tests for OrderedDict subclasses.
 
-- Issue #25188: Add -P/--pgo to test.regrtest to suppress error output when
+- bpo-25188: Add -P/--pgo to test.regrtest to suppress error output when
   running the test suite for the purposes of a PGO build. Initial patch by
   Alecsandru Patrascu.
 
-- Issue #22806: Add ``python -m test --list-tests`` command to list tests.
+- bpo-22806: Add ``python -m test --list-tests`` command to list tests.
 
-- Issue #18174: ``python -m test --huntrleaks ...`` now also checks for leak of
+- bpo-18174: ``python -m test --huntrleaks ...`` now also checks for leak of
   file descriptors. Patch written by Richard Oudkerk.
 
-- Issue #25260: Fix ``python -m test --coverage`` on Windows. Remove the
-  list of ignored directories.
+- bpo-25260: Fix ``python -m test --coverage`` on Windows. Remove the list
+  of ignored directories.
 
-- ``PCbuild\rt.bat`` now accepts an unlimited number of arguments to pass along
-  to regrtest.py.  Previously there was a limit of 9.
+- ``PCbuild\rt.bat`` now accepts an unlimited number of arguments to pass
+  along to regrtest.py.  Previously there was a limit of 9.
 
-- Issue #26583: Skip test_timestamp_overflow in test_import if bytecode
-  files cannot be written.
+- bpo-26583: Skip test_timestamp_overflow in test_import if bytecode files
+  cannot be written.
 
 Build
 -----
 
-- Issue #21277: Don't try to link _ctypes with a ffi_convenience library.
+- bpo-21277: Don't try to link _ctypes with a ffi_convenience library.
 
-- Issue #26884: Fix linking extension modules for cross builds.
-  Patch by Xavier de Gaye.
+- bpo-26884: Fix linking extension modules for cross builds. Patch by Xavier
+  de Gaye.
 
-- Issue #26932: Fixed support of RTLD_* constants defined as enum values,
-  not via macros (in particular on Android).  Patch by Chi Hsuan Yen.
+- bpo-26932: Fixed support of RTLD_* constants defined as enum values, not
+  via macros (in particular on Android).  Patch by Chi Hsuan Yen.
 
-- Issue #22359: Disable the rules for running _freeze_importlib and pgen when
-  cross-compiling.  The output of these programs is normally saved with the
+- bpo-22359: Disable the rules for running _freeze_importlib and pgen when
+  cross- compiling.  The output of these programs is normally saved with the
   source code anyway, and is still regenerated when doing a native build.
   Patch by Xavier de Gaye.
 
-- Issue #21668: Link audioop, _datetime, _ctypes_test modules to libm,
-  except on Mac OS X. Patch written by Chi Hsuan Yen.
+- bpo-21668: Link audioop, _datetime, _ctypes_test modules to libm, except
+  on Mac OS X. Patch written by Chi Hsuan Yen.
 
-- Issue #25702: A --with-lto configure option has been added that will
-  enable link time optimizations at build time during a make profile-opt.
-  Some compilers and toolchains are known to not produce stable code when
-  using LTO, be sure to test things thoroughly before relying on it.
-  It can provide a few % speed up over profile-opt alone.
+- bpo-25702: A --with-lto configure option has been added that will enable
+  link time optimizations at build time during a make profile-opt. Some
+  compilers and toolchains are known to not produce stable code when using
+  LTO, be sure to test things thoroughly before relying on it. It can
+  provide a few % speed up over profile-opt alone.
 
-- Issue #26624: Adds validation of ucrtbase[d].dll version with warning
-  for old versions.
+- bpo-26624: Adds validation of ucrtbase[d].dll version with warning for old
+  versions.
 
-- Issue #17603: Avoid error about nonexistant fileblocks.o file by using a
+- bpo-17603: Avoid error about nonexistant fileblocks.o file by using a
   lower-level check for st_blocks in struct stat.
 
-- Issue #26079: Fixing the build output folder for tix-8.4.3.6. Patch by
-  Bjoern Thiel.
+- bpo-26079: Fixing the build output folder for tix-8.4.3.6. Patch by Bjoern
+  Thiel.
 
-- Issue #26465: Update Windows builds to use OpenSSL 1.0.2g.
+- bpo-26465: Update Windows builds to use OpenSSL 1.0.2g.
 
-- Issue #25348: Added ``--pgo`` and ``--pgo-job`` arguments to
+- bpo-25348: Added ``--pgo`` and ``--pgo-job`` arguments to
   ``PCbuild\build.bat`` for building with Profile-Guided Optimization.  The
   old ``PCbuild\build_pgo.bat`` script is removed.
 
-- Issue #25827: Add support for building with ICC to ``configure``, including
-  new ``--with-icc`` flag.
+- bpo-25827: Add support for building with ICC to ``configure``, including a
+  new ``--with-icc`` flag.
 
-- Issue #25696: Fix installation of Python on UNIX with make -j9.
+- bpo-25696: Fix installation of Python on UNIX with make -j9.
 
-- Issue #24986: It is now possible to build Python on Windows without errors
+- bpo-24986: It is now possible to build Python on Windows without errors
   when external libraries are not available.
 
-- Issue #24421: Compile Modules/_math.c once, before building extensions.
+- bpo-24421: Compile Modules/_math.c once, before building extensions.
   Previously it could fail to compile properly if the math and cmath builds
   were concurrent.
 
-- Issue #26465: Update OS X 10.5+ 32-bit-only installer to build
-  and link with OpenSSL 1.0.2g.
+- bpo-26465: Update OS X 10.5+ 32-bit-only installer to build and link with
+  OpenSSL 1.0.2g.
 
-- Issue #26268: Update Windows builds to use OpenSSL 1.0.2f.
+- bpo-26268: Update Windows builds to use OpenSSL 1.0.2f.
 
-- Issue #25136: Support Apple Xcode 7's new textual SDK stub libraries.
+- bpo-25136: Support Apple Xcode 7's new textual SDK stub libraries.
 
-- Issue #24324: Do not enable unreachable code warnings when using
-  gcc as the option does not work correctly in older versions of gcc
-  and has been silently removed as of gcc-4.5.
+- bpo-24324: Do not enable unreachable code warnings when using gcc as the
+  option does not work correctly in older versions of gcc and has been
+  silently removed as of gcc-4.5.
 
 Windows
 -------
 
-- Issue #27053: Updates make_zip.py to correctly generate library ZIP file.
+- bpo-27053: Updates make_zip.py to correctly generate library ZIP file.
 
-- Issue #26268: Update the prepare_ssl.py script to handle OpenSSL releases
+- bpo-26268: Update the prepare_ssl.py script to handle OpenSSL releases
   that don't include the contents of the include directory (that is, 1.0.2e
   and later).
 
-- Issue #26071: bdist_wininst created binaries fail to start and find
-  32bit Python
+- bpo-26071: bdist_wininst created binaries fail to start and find 32bit
+  Python
 
-- Issue #26073: Update the list of magic numbers in launcher
+- bpo-26073: Update the list of magic numbers in launcher
 
-- Issue #26065: Excludes venv from library when generating embeddable
-  distro.
+- bpo-26065: Excludes venv from library when generating embeddable distro.
 
-- Issue #25022: Removed very outdated PC/example_nt/ directory.
+- bpo-25022: Removed very outdated PC/example_nt/ directory.
 
 Tools/Demos
 -----------
 
-- Issue #26799: Fix python-gdb.py: don't get C types once when the Python code
-  is loaded, but get C types on demand. The C types can change if
-  python-gdb.py is loaded before the Python executable. Patch written by Thomas
+- bpo-26799: Fix python-gdb.py: don't get C types once when the Python code
+  is loaded, but get C types on demand. The C types can change if python-
+  gdb.py is loaded before the Python executable. Patch written by Thomas
   Ilsche.
 
-- Issue #26271: Fix the Freeze tool to properly use flags passed through
+- bpo-26271: Fix the Freeze tool to properly use flags passed through
   configure. Patch by Daniel Shaulov.
 
-- Issue #26489: Add dictionary unpacking support to Tools/parser/unparse.py.
+- bpo-26489: Add dictionary unpacking support to Tools/parser/unparse.py.
   Patch by Guo Ci Teo.
 
-- Issue #26316: Fix variable name typo in Argument Clinic.
+- bpo-26316: Fix variable name typo in Argument Clinic.
 
-- Issue #25440: Fix output of python-config --extension-suffix.
+- bpo-25440: Fix output of python-config --extension-suffix.
 
-- Issue #25154: The pyvenv script has been deprecated in favour of
-  `python3 -m venv`.
+- bpo-25154: The pyvenv script has been deprecated in favour of `python3 -m
+  venv`.
 
 C API
 -----
 
-- Issue #26312: SystemError is now raised in all programming bugs with using
+- bpo-26312: SystemError is now raised in all programming bugs with using
   PyArg_ParseTupleAndKeywords().  RuntimeError did raised before in some
   programming bugs.
 
-- Issue #26198: ValueError is now raised instead of TypeError on buffer
-  overflow in parsing "es#" and "et#" format units.  SystemError is now raised
-  instead of TypeError on programmical error in parsing format string.
+- bpo-26198: ValueError is now raised instead of TypeError on buffer
+  overflow in parsing "es#" and "et#" format units.  SystemError is now
+  raised instead of TypeError on programmical error in parsing format
+  string.
 
 
-What's New in Python 3.5.3?
-===========================
+What's New in Python 3.5.3 final?
+=================================
 
-Release date: 2017-01-17
+*Release date: 2017-01-17*
 
 There were no code changes between 3.5.3rc1 and 3.5.3 final.
 
 
+
 What's New in Python 3.5.3 release candidate 1?
 ===============================================
 
-Release date: 2017-01-02
+*Release date: 2017-01-02*
 
 Core and Builtins
 -----------------
 
-- Issue #29073: bytearray formatting no longer truncates on first null byte.
+- bpo-29073: bytearray formatting no longer truncates on first null byte.
 
-- Issue #28932: Do not include <sys/random.h> if it does not exist.
+- bpo-28932: Do not include <sys/random.h> if it does not exist.
 
-- Issue #28147: Fix a memory leak in split-table dictionaries: setattr()
-  must not convert combined table into split table.
+- bpo-28147: Fix a memory leak in split-table dictionaries: setattr() must
+  not convert combined table into split table.
 
-- Issue #25677: Correct the positioning of the syntax error caret for
-  indented blocks.  Based on patch by Michael Layzell.
+- bpo-25677: Correct the positioning of the syntax error caret for indented
+  blocks. Based on patch by Michael Layzell.
 
-- Issue #29000: Fixed bytes formatting of octals with zero padding in alternate
+- bpo-29000: Fixed bytes formatting of octals with zero padding in alternate
   form.
 
-- Issue #28512: Fixed setting the offset attribute of SyntaxError by
+- bpo-28512: Fixed setting the offset attribute of SyntaxError by
   PyErr_SyntaxLocationEx() and PyErr_SyntaxLocationObject().
 
-- Issue #28991:  functools.lru_cache() was susceptible to an obscure reentrancy
+- bpo-28991: functools.lru_cache() was susceptible to an obscure reentrancy
   bug caused by a monkey-patched len() function.
 
-- Issue #28648: Fixed crash in Py_DecodeLocale() in debug build on Mac OS X
+- bpo-28648: Fixed crash in Py_DecodeLocale() in debug build on Mac OS X
   when decode astral characters.  Patch by Xiang Zhang.
 
-- Issue #19398: Extra slash no longer added to sys.path components in case of
-  empty compile-time PYTHONPATH components.
+- bpo-19398: Extra slash no longer added to sys.path components in case of
+  empty compile- time PYTHONPATH components.
 
-- Issue #28426: Fixed potential crash in PyUnicode_AsDecodedObject() in debug
+- bpo-28426: Fixed potential crash in PyUnicode_AsDecodedObject() in debug
   build.
 
-- Issue #23782: Fixed possible memory leak in _PyTraceback_Add() and exception
+- bpo-23782: Fixed possible memory leak in _PyTraceback_Add() and exception
   loss in PyTraceBack_Here().
 
-- Issue #28379: Added sanity checks and tests for PyUnicode_CopyCharacters().
+- bpo-28379: Added sanity checks and tests for PyUnicode_CopyCharacters().
   Patch by Xiang Zhang.
 
-- Issue #28376: The type of long range iterator is now registered as Iterator.
+- bpo-28376: The type of long range iterator is now registered as Iterator.
   Patch by Oren Milman.
 
-- Issue #28376: The constructor of range_iterator now checks that step is not 0.
-  Patch by Oren Milman.
+- bpo-28376: The constructor of range_iterator now checks that step is not
+  0. Patch by Oren Milman.
 
-- Issue #26906: Resolving special methods of uninitialized type now causes
+- bpo-26906: Resolving special methods of uninitialized type now causes
   implicit initialization of the type instead of a fail.
 
-- Issue #18287: PyType_Ready() now checks that tp_name is not NULL.
-  Original patch by Niklas Koep.
+- bpo-18287: PyType_Ready() now checks that tp_name is not NULL. Original
+  patch by Niklas Koep.
 
-- Issue #24098: Fixed possible crash when AST is changed in process of
+- bpo-24098: Fixed possible crash when AST is changed in process of
   compiling it.
 
-- Issue #28350: String constants with null character no longer interned.
+- bpo-28350: String constants with null character no longer interned.
 
-- Issue #26617: Fix crash when GC runs during weakref callbacks.
+- bpo-26617: Fix crash when GC runs during weakref callbacks.
 
-- Issue #27942: String constants now interned recursively in tuples and frozensets.
+- bpo-27942: String constants now interned recursively in tuples and
+  frozensets.
 
-- Issue #21578: Fixed misleading error message when ImportError called with
+- bpo-21578: Fixed misleading error message when ImportError called with
   invalid keyword args.
 
-- Issue #28203: Fix incorrect type in error message from
-  ``complex(1.0, {2:3})``. Patch by Soumya Sharma.
+- bpo-28203: Fix incorrect type in error message from ``complex(1.0,
+  {2:3})``. Patch by Soumya Sharma.
 
-- Issue #27955: Fallback on reading /dev/urandom device when the getrandom()
+- bpo-27955: Fallback on reading /dev/urandom device when the getrandom()
   syscall fails with EPERM, for example when blocked by SECCOMP.
 
-- Issue #28131: Fix a regression in zipimport's compile_source().  zipimport
+- bpo-28131: Fix a regression in zipimport's compile_source().  zipimport
   should use the same optimization level as the interpreter.
 
-- Issue #25221: Fix corrupted result from PyLong_FromLong(0) when
-  Python is compiled with NSMALLPOSINTS = 0.
+- bpo-25221: Fix corrupted result from PyLong_FromLong(0) when Python is
+  compiled with NSMALLPOSINTS = 0.
 
-- Issue #25758: Prevents zipimport from unnecessarily encoding a filename
+- bpo-25758: Prevents zipimport from unnecessarily encoding a filename
   (patch by Eryk Sun)
 
-- Issue #28189: dictitems_contains no longer swallows compare errors.
-  (Patch by Xiang Zhang)
+- bpo-28189: dictitems_contains no longer swallows compare errors. (Patch by
+  Xiang Zhang)
 
-- Issue #27812: Properly clear out a generator's frame's backreference to the
+- bpo-27812: Properly clear out a generator's frame's backreference to the
   generator to prevent crashes in frame.clear().
 
-- Issue #27811: Fix a crash when a coroutine that has not been awaited is
+- bpo-27811: Fix a crash when a coroutine that has not been awaited is
   finalized with warnings-as-errors enabled.
 
-- Issue #27587: Fix another issue found by PVS-Studio: Null pointer check
-  after use of 'def' in _PyState_AddModule().
-  Initial patch by Christian Heimes.
+- bpo-27587: Fix another issue found by PVS-Studio: Null pointer check after
+  use of 'def' in _PyState_AddModule(). Initial patch by Christian Heimes.
 
-- Issue #26020: set literal evaluation order did not match documented behaviour.
+- bpo-26020: set literal evaluation order did not match documented
+  behaviour.
 
-- Issue #27782: Multi-phase extension module import now correctly allows the
+- bpo-27782: Multi-phase extension module import now correctly allows the
   ``m_methods`` field to be used to add module level functions to instances
   of non-module types returned from ``Py_create_mod``. Patch by Xiang Zhang.
 
-- Issue #27936: The round() function accepted a second None argument
-  for some types but not for others.  Fixed the inconsistency by
-  accepting None for all numeric types.
+- bpo-27936: The round() function accepted a second None argument for some
+  types but not for others.  Fixed the inconsistency by accepting None for
+  all numeric types.
 
-- Issue #27487: Warn if a submodule argument to "python -m" or
+- bpo-27487: Warn if a submodule argument to "python -m" or
   runpy.run_module() is found in sys.modules after parent packages are
   imported, but before the submodule is executed.
 
-- Issue #27558: Fix a SystemError in the implementation of "raise" statement.
+- bpo-27558: Fix a SystemError in the implementation of "raise" statement.
   In a brand new thread, raise a RuntimeError since there is no active
   exception to reraise. Patch written by Xiang Zhang.
 
-- Issue #27419: Standard __import__() no longer look up "__import__" in globals
+- bpo-27419: Standard __import__() no longer look up "__import__" in globals
   or builtins for importing submodules or "from import".  Fixed handling an
-  error of non-string package name.
+  error of non- string package name.
 
-- Issue #27083: Respect the PYTHONCASEOK environment variable under Windows.
+- bpo-27083: Respect the PYTHONCASEOK environment variable under Windows.
 
-- Issue #27514: Make having too many statically nested blocks a SyntaxError
+- bpo-27514: Make having too many statically nested blocks a SyntaxError
   instead of SystemError.
 
-- Issue #27473: Fixed possible integer overflow in bytes and bytearray
-  concatenations.  Patch by Xiang Zhang.
+- bpo-27473: Fixed possible integer overflow in bytes and bytearray
+  concatenations. Patch by Xiang Zhang.
 
-- Issue #27507: Add integer overflow check in bytearray.extend().  Patch by
+- bpo-27507: Add integer overflow check in bytearray.extend().  Patch by
   Xiang Zhang.
 
-- Issue #27581: Don't rely on wrapping for overflow check in
+- bpo-27581: Don't rely on wrapping for overflow check in
   PySequence_Tuple().  Patch by Xiang Zhang.
 
-- Issue #27443: __length_hint__() of bytearray iterators no longer return a
+- bpo-27443: __length_hint__() of bytearray iterators no longer return a
   negative integer for a resized bytearray.
 
-- Issue #27942: Fix memory leak in codeobject.c
+- bpo-27942: Fix memory leak in codeobject.c
 
 Library
 -------
 
-- Issue #15812: inspect.getframeinfo() now correctly shows the first line of
-  a context.  Patch by Sam Breese.
+- bpo-15812: inspect.getframeinfo() now correctly shows the first line of a
+  context. Patch by Sam Breese.
 
-- Issue #29094: Offsets in a ZIP file created with extern file object and modes
+- bpo-29094: Offsets in a ZIP file created with extern file object and modes
   "w" and "x" now are relative to the start of the file.
 
-- Issue #13051: Fixed recursion errors in large or resized
+- bpo-13051: Fixed recursion errors in large or resized
   curses.textpad.Textbox.  Based on patch by Tycho Andersen.
 
-- Issue #29119: Fix weakrefs in the pure python version of
-  collections.OrderedDict move_to_end() method.
-  Contributed by Andra Bogildea.
+- bpo-29119: Fix weakrefs in the pure python version of
+  collections.OrderedDict move_to_end() method. Contributed by Andra
+  Bogildea.
 
-- Issue #9770: curses.ascii predicates now work correctly with negative
+- bpo-9770: curses.ascii predicates now work correctly with negative
   integers.
 
-- Issue #28427: old keys should not remove new values from
-  WeakValueDictionary when collecting from another thread.
+- bpo-28427: old keys should not remove new values from WeakValueDictionary
+  when collecting from another thread.
 
-- Issue #28923: Remove editor artifacts from Tix.py.
+- bpo-28923: Remove editor artifacts from Tix.py.
 
-- Issue #28871: Fixed a crash when deallocate deep ElementTree.
+- bpo-28871: Fixed a crash when deallocate deep ElementTree.
 
-- Issue #19542: Fix bugs in WeakValueDictionary.setdefault() and
-  WeakValueDictionary.pop() when a GC collection happens in another
-  thread.
+- bpo-19542: Fix bugs in WeakValueDictionary.setdefault() and
+  WeakValueDictionary.pop() when a GC collection happens in another thread.
 
-- Issue #20191: Fixed a crash in resource.prlimit() when pass a sequence that
+- bpo-20191: Fixed a crash in resource.prlimit() when pass a sequence that
   doesn't own its elements as limits.
 
-- Issue #28779: multiprocessing.set_forkserver_preload() would crash the
-  forkserver process if a preloaded module instantiated some
-  multiprocessing objects such as locks.
+- bpo-28779: multiprocessing.set_forkserver_preload() would crash the
+  forkserver process if a preloaded module instantiated some multiprocessing
+  objects such as locks.
 
-- Issue #28847: dbm.dumb now supports reading read-only files and no longer
+- bpo-28847: dbm.dumb now supports reading read-only files and no longer
   writes the index file when it is not changed.
 
-- Issue #25659: In ctypes, prevent a crash calling the from_buffer() and
+- bpo-25659: In ctypes, prevent a crash calling the from_buffer() and
   from_buffer_copy() methods on abstract classes like Array.
 
-- Issue #28732: Fix crash in os.spawnv() with no elements in args
+- bpo-28732: Fix crash in os.spawnv() with no elements in args
 
-- Issue #28485: Always raise ValueError for negative
+- bpo-28485: Always raise ValueError for negative
   compileall.compile_dir(workers=...) parameter, even when multithreading is
   unavailable.
 
-- Issue #28387: Fixed possible crash in _io.TextIOWrapper deallocator when
-  the garbage collector is invoked in other thread.  Based on patch by
-  Sebastian Cufre.
+- bpo-28387: Fixed possible crash in _io.TextIOWrapper deallocator when the
+  garbage collector is invoked in other thread.  Based on patch by Sebastian
+  Cufre.
 
-- Issue #27517: LZMA compressor and decompressor no longer raise exceptions if
+- bpo-27517: LZMA compressor and decompressor no longer raise exceptions if
   given empty data twice.  Patch by Benjamin Fogle.
 
-- Issue #28549: Fixed segfault in curses's addch() with ncurses6.
+- bpo-28549: Fixed segfault in curses's addch() with ncurses6.
 
-- Issue #28449: tarfile.open() with mode "r" or "r:" now tries to open a tar
-  file with compression before trying to open it without compression.  Otherwise
-  it had 50% chance failed with ignore_zeros=True.
+- bpo-28449: tarfile.open() with mode "r" or "r:" now tries to open a tar
+  file with compression before trying to open it without compression.
+  Otherwise it had 50% chance failed with ignore_zeros=True.
 
-- Issue #23262: The webbrowser module now supports Firefox 36+ and derived
+- bpo-23262: The webbrowser module now supports Firefox 36+ and derived
   browsers.  Based on patch by Oleg Broytman.
 
-- Issue #27939: Fixed bugs in tkinter.ttk.LabeledScale and tkinter.Scale caused
+- bpo-27939: Fixed bugs in tkinter.ttk.LabeledScale and tkinter.Scale caused
   by representing the scale as float value internally in Tk.  tkinter.IntVar
   now works if float value is set to underlying Tk variable.
 
-- Issue #28255: calendar.TextCalendar().prmonth() no longer prints a space
-  at the start of new line after printing a month's calendar.  Patch by
-  Xiang Zhang.
+- bpo-28255: calendar.TextCalendar().prmonth() no longer prints a space at
+  the start of new line after printing a month's calendar.  Patch by Xiang
+  Zhang.
 
-- Issue #20491: The textwrap.TextWrapper class now honors non-breaking spaces.
+- bpo-20491: The textwrap.TextWrapper class now honors non-breaking spaces.
   Based on patch by Kaarle Ritvanen.
 
-- Issue #28353: os.fwalk() no longer fails on broken links.
+- bpo-28353: os.fwalk() no longer fails on broken links.
 
-- Issue #25464: Fixed HList.header_exists() in tkinter.tix module by addin
-  workaround to Tix library bug.
+- bpo-25464: Fixed HList.header_exists() in tkinter.tix module by addin a
+  workaround to Tix library bug.
 
-- Issue #28488: shutil.make_archive() no longer add entry "./" to ZIP archive.
+- bpo-28488: shutil.make_archive() no longer add entry "./" to ZIP archive.
 
-- Issue #24452: Make webbrowser support Chrome on Mac OS X.
+- bpo-24452: Make webbrowser support Chrome on Mac OS X.
 
-- Issue #20766: Fix references leaked by pdb in the handling of SIGINT
+- bpo-20766: Fix references leaked by pdb in the handling of SIGINT
   handlers.
 
-- Issue #26293: Fixed writing ZIP files that starts not from the start of the
+- bpo-26293: Fixed writing ZIP files that starts not from the start of the
   file.  Offsets in ZIP file now are relative to the start of the archive in
   conforming to the specification.
 
-- Issue #28321: Fixed writing non-BMP characters with binary format in plistlib.
+- bpo-28321: Fixed writing non-BMP characters with binary format in
+  plistlib.
 
-- Issue #28322: Fixed possible crashes when unpickle itertools objects from
+- bpo-28322: Fixed possible crashes when unpickle itertools objects from
   incorrect pickle data.  Based on patch by John Leitch.
 
 - Fix possible integer overflows and crashes in the mmap module with unusual
   usage patterns.
 
-- Issue #1703178: Fix the ability to pass the --link-objects option to the
+- bpo-1703178: Fix the ability to pass the --link-objects option to the
   distutils build_ext command.
 
-- Issue #28253: Fixed calendar functions for extreme months: 0001-01
-  and 9999-12.
+- bpo-28253: Fixed calendar functions for extreme months: 0001-01 and
+  9999-12.
 
-  Methods itermonthdays() and itermonthdays2() are reimplemented so
-  that they don't call itermonthdates() which can cause datetime.date
+  Methods itermonthdays() and itermonthdays2() are reimplemented so that
+  they don't call itermonthdates() which can cause datetime.date
   under/overflow.
 
-- Issue #28275: Fixed possible use after free in the decompress()
-  methods of the LZMADecompressor and BZ2Decompressor classes.
-  Original patch by John Leitch.
+- bpo-28275: Fixed possible use after free in the decompress() methods of
+  the LZMADecompressor and BZ2Decompressor classes. Original patch by John
+  Leitch.
 
-- Issue #27897: Fixed possible crash in sqlite3.Connection.create_collation()
+- bpo-27897: Fixed possible crash in sqlite3.Connection.create_collation()
   if pass invalid string-like object as a name.  Patch by Xiang Zhang.
 
-- Issue #18893: Fix invalid exception handling in Lib/ctypes/macholib/dyld.py.
+- bpo-18893: Fix invalid exception handling in Lib/ctypes/macholib/dyld.py.
   Patch by Madison May.
 
-- Issue #27611: Fixed support of default root window in the tkinter.tix module.
+- bpo-27611: Fixed support of default root window in the tkinter.tix module.
 
-- Issue #27348: In the traceback module, restore the formatting of exception
+- bpo-27348: In the traceback module, restore the formatting of exception
   messages like "Exception: None".  This fixes a regression introduced in
   3.5a2.
 
-- Issue #25651: Allow falsy values to be used for msg parameter of subTest().
+- bpo-25651: Allow falsy values to be used for msg parameter of subTest().
 
-- Issue #27932: Prevent memory leak in win32_ver().
+- bpo-27932: Prevent memory leak in win32_ver().
 
 - Fix UnboundLocalError in socket._sendfile_use_sendfile.
 
-- Issue #28075: Check for ERROR_ACCESS_DENIED in Windows implementation of
+- bpo-28075: Check for ERROR_ACCESS_DENIED in Windows implementation of
   os.stat().  Patch by Eryk Sun.
 
-- Issue #25270: Prevent codecs.escape_encode() from raising SystemError when
-  an empty bytestring is passed.
+- bpo-25270: Prevent codecs.escape_encode() from raising SystemError when an
+  empty bytestring is passed.
 
-- Issue #28181: Get antigravity over HTTPS. Patch by Kaartic Sivaraam.
+- bpo-28181: Get antigravity over HTTPS. Patch by Kaartic Sivaraam.
 
-- Issue #25895: Enable WebSocket URL schemes in urllib.parse.urljoin.
-  Patch by Gergely Imreh and Markus Holtermann.
+- bpo-25895: Enable WebSocket URL schemes in urllib.parse.urljoin. Patch by
+  Gergely Imreh and Markus Holtermann.
 
-- Issue #27599: Fixed buffer overrun in binascii.b2a_qp() and binascii.a2b_qp().
+- bpo-27599: Fixed buffer overrun in binascii.b2a_qp() and
+  binascii.a2b_qp().
 
-- Issue #19003:m email.generator now replaces only ``\r`` and/or ``\n`` line
+- bpo-19003: m email.generator now replaces only ``\r`` and/or ``\n`` line
   endings, per the RFC, instead of all unicode line endings.
 
-- Issue #28019: itertools.count() no longer rounds non-integer step in range
+- bpo-28019: itertools.count() no longer rounds non-integer step in range
   between 1.0 and 2.0 to 1.
 
-- Issue #25969: Update the lib2to3 grammar to handle the unpacking
+- bpo-25969: Update the lib2to3 grammar to handle the unpacking
   generalizations added in 3.5.
 
-- Issue #14977: mailcap now respects the order of the lines in the mailcap
+- bpo-14977: mailcap now respects the order of the lines in the mailcap
   files ("first match"), as required by RFC 1542.  Patch by Michael Lazar.
 
-- Issue #24594: Validates persist parameter when opening MSI database
+- bpo-24594: Validates persist parameter when opening MSI database
 
-- Issue #17582: xml.etree.ElementTree nows preserves whitespaces in attributes
+- bpo-17582: xml.etree.ElementTree nows preserves whitespaces in attributes
   (Patch by Duane Griffin.  Reviewed and approved by Stefan Behnel.)
 
-- Issue #28047: Fixed calculation of line length used for the base64 CTE
-  in the new email policies.
+- bpo-28047: Fixed calculation of line length used for the base64 CTE in the
+  new email policies.
 
-- Issue #27445: Don't pass str(_charset) to MIMEText.set_payload().
-  Patch by Claude Paroz.
+- bpo-27445: Don't pass str(_charset) to MIMEText.set_payload(). Patch by
+  Claude Paroz.
 
-- Issue #22450: urllib now includes an ``Accept: */*`` header among the
-  default headers.  This makes the results of REST API requests more
-  consistent and predictable especially when proxy servers are involved.
+- bpo-22450: urllib now includes an ``Accept: */*`` header among the default
+  headers. This makes the results of REST API requests more consistent and
+  predictable especially when proxy servers are involved.
 
 - lib2to3.pgen3.driver.load_grammar() now creates a stable cache file
   between runs given the same Grammar.txt input regardless of the hash
   randomization setting.
 
-- Issue #27570: Avoid zero-length memcpy() etc calls with null source
-  pointers in the "ctypes" and "array" modules.
+- bpo-27570: Avoid zero-length memcpy() etc calls with null source pointers
+  in the "ctypes" and "array" modules.
 
-- Issue #22233: Break email header lines *only* on the RFC specified CR and LF
-  characters, not on arbitrary unicode line breaks.  This also fixes a bug in
-  HTTP header parsing.
+- bpo-22233: Break email header lines *only* on the RFC specified CR and LF
+  characters, not on arbitrary unicode line breaks.  This also fixes a bug
+  in HTTP header parsing.
 
-- Issue #27988: Fix email iter_attachments incorrect mutation of payload list.
+- bpo-27988: Fix email iter_attachments incorrect mutation of payload list.
 
-- Issue #27691: Fix ssl module's parsing of GEN_RID subject alternative name
+- bpo-27691: Fix ssl module's parsing of GEN_RID subject alternative name
   fields in X.509 certs.
 
-- Issue #27850: Remove 3DES from ssl module's default cipher list to counter
+- bpo-27850: Remove 3DES from ssl module's default cipher list to counter
   measure sweet32 attack (CVE-2016-2183).
 
-- Issue #27766: Add ChaCha20 Poly1305 to ssl module's default ciper list.
+- bpo-27766: Add ChaCha20 Poly1305 to ssl module's default ciper list.
   (Required OpenSSL 1.1.0 or LibreSSL).
 
-- Issue #26470: Port ssl and hashlib module to OpenSSL 1.1.0.
+- bpo-26470: Port ssl and hashlib module to OpenSSL 1.1.0.
 
-- Remove support for passing a file descriptor to os.access. It never worked but
-  previously didn't raise.
+- Remove support for passing a file descriptor to os.access. It never worked
+  but previously didn't raise.
 
-- Issue #12885: Fix error when distutils encounters symlink.
+- bpo-12885: Fix error when distutils encounters symlink.
 
-- Issue #27881: Fixed possible bugs when setting sqlite3.Connection.isolation_level.
-  Based on patch by Xiang Zhang.
+- bpo-27881: Fixed possible bugs when setting
+  sqlite3.Connection.isolation_level. Based on patch by Xiang Zhang.
 
-- Issue #27861: Fixed a crash in sqlite3.Connection.cursor() when a factory
+- bpo-27861: Fixed a crash in sqlite3.Connection.cursor() when a factory
   creates not a cursor.  Patch by Xiang Zhang.
 
-- Issue #19884: Avoid spurious output on OS X with Gnu Readline.
+- bpo-19884: Avoid spurious output on OS X with Gnu Readline.
 
-- Issue #27706: Restore deterministic behavior of random.Random().seed()
-  for string seeds using seeding version 1.  Allows sequences of calls
-  to random() to exactly match those obtained in Python 2.
-  Patch by Nofar Schnider.
+- bpo-27706: Restore deterministic behavior of random.Random().seed() for
+  string seeds using seeding version 1.  Allows sequences of calls to
+  random() to exactly match those obtained in Python 2. Patch by Nofar
+  Schnider.
 
-- Issue #10513: Fix a regression in Connection.commit().  Statements should
-  not be reset after a commit.
+- bpo-10513: Fix a regression in Connection.commit().  Statements should not
+  be reset after a commit.
 
-- A new version of typing.py from https://github.com/python/typing:
-  - Collection (only for 3.6) (Issue #27598)
-  - Add FrozenSet to __all__ (upstream #261)
-  - fix crash in _get_type_vars() (upstream #259)
-  - Remove the dict constraint in ForwardRef._eval_type (upstream #252)
+- A new version of typing.py from https://github.com/python/typing: -
+  Collection (only for 3.6) (Issue #27598) - Add FrozenSet to __all__
+  (upstream #261) - fix crash in _get_type_vars() (upstream #259) - Remove
+  the dict constraint in ForwardRef._eval_type (upstream #252)
 
-- Issue #27539: Fix unnormalised ``Fraction.__pow__`` result in the case
-  of negative exponent and negative base.
+- bpo-27539: Fix unnormalised ``Fraction.__pow__`` result in the case of
+  negative exponent and negative base.
 
-- Issue #21718: cursor.description is now available for queries using CTEs.
+- bpo-21718: cursor.description is now available for queries using CTEs.
 
-- Issue #2466: posixpath.ismount now correctly recognizes mount points which
+- bpo-2466: posixpath.ismount now correctly recognizes mount points which
   the user does not have permission to access.
 
-- Issue #27773: Correct some memory management errors server_hostname in
+- bpo-27773: Correct some memory management errors server_hostname in
   _ssl.wrap_socket().
 
-- Issue #26750: unittest.mock.create_autospec() now works properly for
+- bpo-26750: unittest.mock.create_autospec() now works properly for
   subclasses of property() and other data descriptors.
 
-- In the curses module, raise an error if window.getstr() or window.instr() is
-  passed a negative value.
+- In the curses module, raise an error if window.getstr() or window.instr()
+  is passed a negative value.
 
-- Issue #27783: Fix possible usage of uninitialized memory in
+- bpo-27783: Fix possible usage of uninitialized memory in
   operator.methodcaller.
 
-- Issue #27774: Fix possible Py_DECREF on unowned object in _sre.
+- bpo-27774: Fix possible Py_DECREF on unowned object in _sre.
 
-- Issue #27760: Fix possible integer overflow in binascii.b2a_qp.
+- bpo-27760: Fix possible integer overflow in binascii.b2a_qp.
 
-- Issue #27758: Fix possible integer overflow in the _csv module for large
+- bpo-27758: Fix possible integer overflow in the _csv module for large
   record lengths.
 
-- Issue #27568: Prevent HTTPoxy attack (CVE-2016-1000110). Ignore the
-  HTTP_PROXY variable when REQUEST_METHOD environment is set, which indicates
-  that the script is in CGI mode.
+- bpo-27568: Prevent HTTPoxy attack (CVE-2016-1000110). Ignore the
+  HTTP_PROXY variable when REQUEST_METHOD environment is set, which
+  indicates that the script is in CGI mode.
 
-- Issue #27656: Do not assume sched.h defines any SCHED_* constants.
+- bpo-27656: Do not assume sched.h defines any SCHED_* constants.
 
-- Issue #27130: In the "zlib" module, fix handling of large buffers
-  (typically 4 GiB) when compressing and decompressing.  Previously, inputs
-  were limited to 4 GiB, and compression and decompression operations did not
+- bpo-27130: In the "zlib" module, fix handling of large buffers (typically
+  4 GiB) when compressing and decompressing.  Previously, inputs were
+  limited to 4 GiB, and compression and decompression operations did not
   properly handle results of 4 GiB.
 
-- Issue #27533: Release GIL in nt._isdir
+- bpo-27533: Release GIL in nt._isdir
 
-- Issue #17711: Fixed unpickling by the persistent ID with protocol 0.
-  Original patch by Alexandre Vassalotti.
+- bpo-17711: Fixed unpickling by the persistent ID with protocol 0. Original
+  patch by Alexandre Vassalotti.
 
-- Issue #27522: Avoid an unintentional reference cycle in email.feedparser.
+- bpo-27522: Avoid an unintentional reference cycle in email.feedparser.
 
-- Issue #26844: Fix error message for imp.find_module() to refer to 'path'
+- bpo-26844: Fix error message for imp.find_module() to refer to 'path'
   instead of 'name'. Patch by Lev Maximov.
 
-- Issue #23804: Fix SSL zero-length recv() calls to not block and not raise
-  an error about unclean EOF.
+- bpo-23804: Fix SSL zero-length recv() calls to not block and not raise an
+  error about unclean EOF.
 
-- Issue #27466: Change time format returned by http.cookie.time2netscape,
+- bpo-27466: Change time format returned by http.cookie.time2netscape,
   confirming the netscape cookie format and making it consistent with
   documentation.
 
-- Issue #26664: Fix activate.fish by removing mis-use of ``$``.
+- bpo-26664: Fix activate.fish by removing mis-use of ``$``.
 
-- Issue #22115: Fixed tracing Tkinter variables: trace_vdelete() with wrong
+- bpo-22115: Fixed tracing Tkinter variables: trace_vdelete() with wrong
   mode no longer break tracing, trace_vinfo() now always returns a list of
   pairs of strings, tracing in the "u" mode now works.
 
@@ -4298,1340 +4839,1373 @@ Library
   UnboundLocalError when lazy-loading a module that was already put into
   sys.modules.
 
-- Issue #27079: Fixed curses.ascii functions isblank(), iscntrl() and ispunct().
+- bpo-27079: Fixed curses.ascii functions isblank(), iscntrl() and
+  ispunct().
 
-- Issue #26754: Some functions (compile() etc) accepted a filename argument
+- bpo-26754: Some functions (compile() etc) accepted a filename argument
   encoded as an iterable of integers. Now only strings and byte-like objects
   are accepted.
 
-- Issue #27048: Prevents distutils failing on Windows when environment
+- bpo-27048: Prevents distutils failing on Windows when environment
   variables contain non-ASCII characters
 
-- Issue #27330: Fixed possible leaks in the ctypes module.
+- bpo-27330: Fixed possible leaks in the ctypes module.
 
-- Issue #27238: Got rid of bare excepts in the turtle module.  Original patch
+- bpo-27238: Got rid of bare excepts in the turtle module.  Original patch
   by Jelle Zijlstra.
 
-- Issue #27122: When an exception is raised within the context being managed
-  by a contextlib.ExitStack() and one of the exit stack generators
-  catches and raises it in a chain, do not re-raise the original exception
-  when exiting, let the new chained one through.  This avoids the PEP 479
-  bug described in issue25782.
+- bpo-27122: When an exception is raised within the context being managed by
+  a contextlib.ExitStack() and one of the exit stack generators catches and
+  raises it in a chain, do not re-raise the original exception when exiting,
+  let the new chained one through.  This avoids the PEP 479 bug described in
+  issue25782.
+
+Security
+--------
+
+- bpo-27278: Fix os.urandom() implementation using getrandom() on Linux.
+  Truncate size to INT_MAX and loop until we collected enough random bytes,
+  instead of casting a directly Py_ssize_t to int.
 
-- [Security] Issue #27278: Fix os.urandom() implementation using getrandom() on
-  Linux.  Truncate size to INT_MAX and loop until we collected enough random
-  bytes, instead of casting a directly Py_ssize_t to int.
+Library
+-------
 
-- Issue #26386: Fixed ttk.TreeView selection operations with item id's
+- bpo-26386: Fixed ttk.TreeView selection operations with item id's
   containing spaces.
 
-- [Security] Issue #22636: Avoid shell injection problems with
-  ctypes.util.find_library().
+Security
+--------
+
+- bpo-22636: Avoid shell injection problems with ctypes.util.find_library().
+
+Library
+-------
 
-- Issue #16182: Fix various functions in the "readline" module to use the
-  locale encoding, and fix get_begidx() and get_endidx() to return code point
-  indexes.
+- bpo-16182: Fix various functions in the "readline" module to use the
+  locale encoding, and fix get_begidx() and get_endidx() to return code
+  point indexes.
 
-- Issue #27392: Add loop.connect_accepted_socket().
-  Patch by Jim Fulton.
+- bpo-27392: Add loop.connect_accepted_socket(). Patch by Jim Fulton.
 
-- Issue #27930: Improved behaviour of logging.handlers.QueueListener.
-  Thanks to Paulo Andrade and Petr Viktorin for the analysis and patch.
+- bpo-27930: Improved behaviour of logging.handlers.QueueListener. Thanks to
+  Paulo Andrade and Petr Viktorin for the analysis and patch.
 
-- Issue #21201: Improves readability of multiprocessing error message.  Thanks
+- bpo-21201: Improves readability of multiprocessing error message.  Thanks
   to Wojciech Walczak for patch.
 
-- Issue #27456: asyncio: Set TCP_NODELAY by default.
+- bpo-27456: asyncio: Set TCP_NODELAY by default.
 
-- Issue #27906: Fix socket accept exhaustion during high TCP traffic.
-  Patch by Kevin Conway.
+- bpo-27906: Fix socket accept exhaustion during high TCP traffic. Patch by
+  Kevin Conway.
 
-- Issue #28174: Handle when SO_REUSEPORT isn't properly supported.
-  Patch by Seth Michael Larson.
+- bpo-28174: Handle when SO_REUSEPORT isn't properly supported. Patch by
+  Seth Michael Larson.
 
-- Issue #26654: Inspect functools.partial in asyncio.Handle.__repr__.
-  Patch by iceboy.
+- bpo-26654: Inspect functools.partial in asyncio.Handle.__repr__. Patch by
+  iceboy.
 
-- Issue #26909: Fix slow pipes IO in asyncio.
-  Patch by INADA Naoki.
+- bpo-26909: Fix slow pipes IO in asyncio. Patch by INADA Naoki.
 
-- Issue #28176: Fix callbacks race in asyncio.SelectorLoop.sock_connect.
+- bpo-28176: Fix callbacks race in asyncio.SelectorLoop.sock_connect.
 
-- Issue #27759: Fix selectors incorrectly retain invalid file descriptors.
+- bpo-27759: Fix selectors incorrectly retain invalid file descriptors.
   Patch by Mark Williams.
 
-- Issue #28368: Refuse monitoring processes if the child watcher has
-  no loop attached.
-  Patch by Vincent Michel.
+- bpo-28368: Refuse monitoring processes if the child watcher has no loop
+  attached. Patch by Vincent Michel.
 
-- Issue #28369: Raise RuntimeError when transport's FD is used with
-  add_reader, add_writer, etc.
+- bpo-28369: Raise RuntimeError when transport's FD is used with add_reader,
+  add_writer, etc.
 
-- Issue #28370: Speedup asyncio.StreamReader.readexactly.
-  Patch by Коренберг Марк.
+- bpo-28370: Speedup asyncio.StreamReader.readexactly. Patch by Коренберг
+  Марк.
 
-- Issue #28371: Deprecate passing asyncio.Handles to run_in_executor.
+- bpo-28371: Deprecate passing asyncio.Handles to run_in_executor.
 
-- Issue #28372: Fix asyncio to support formatting of non-python coroutines.
+- bpo-28372: Fix asyncio to support formatting of non-python coroutines.
 
-- Issue #28399: Remove UNIX socket from FS before binding.
-  Patch by Коренберг Марк.
+- bpo-28399: Remove UNIX socket from FS before binding. Patch by Коренберг
+  Марк.
 
-- Issue #27972: Prohibit Tasks to await on themselves.
+- bpo-27972: Prohibit Tasks to await on themselves.
 
-- Issue #26923: Fix asyncio.Gather to refuse being cancelled once all
-  children are done.
-  Patch by Johannes Ebke.
+- bpo-26923: Fix asyncio.Gather to refuse being cancelled once all children
+  are done. Patch by Johannes Ebke.
 
-- Issue #26796: Don't configure the number of workers for default
-  threadpool executor.
-  Initial patch by Hans Lawrenz.
+- bpo-26796: Don't configure the number of workers for default threadpool
+  executor. Initial patch by Hans Lawrenz.
 
-- Issue #28600: Optimize loop.call_soon().
+- bpo-28600: Optimize loop.call_soon().
 
-- Issue #28613: Fix get_event_loop() return the current loop if
-  called from coroutines/callbacks.
+- bpo-28613: Fix get_event_loop() return the current loop if called from
+  coroutines/callbacks.
 
-- Issue #28639: Fix inspect.isawaitable to always return bool
-  Patch by Justin Mayfield.
+- bpo-28639: Fix inspect.isawaitable to always return bool Patch by Justin
+  Mayfield.
 
-- Issue #28652: Make loop methods reject socket kinds they do not support.
+- bpo-28652: Make loop methods reject socket kinds they do not support.
 
-- Issue #28653: Fix a refleak in functools.lru_cache.
+- bpo-28653: Fix a refleak in functools.lru_cache.
 
-- Issue #28703: Fix asyncio.iscoroutinefunction to handle Mock objects.
+- bpo-28703: Fix asyncio.iscoroutinefunction to handle Mock objects.
 
-- Issue #24142: Reading a corrupt config file left the parser in an
-  invalid state.  Original patch by Florian Höch.
+- bpo-24142: Reading a corrupt config file left the parser in an invalid
+  state.  Original patch by Florian Höch.
 
-- Issue #28990: Fix SSL hanging if connection is closed before handshake
-  completed.
-  (Patch by HoHo-Ho)
+- bpo-28990: Fix SSL hanging if connection is closed before handshake
+  completed. (Patch by HoHo-Ho)
 
 IDLE
 ----
 
-- Issue #15308: Add 'interrupt execution' (^C) to Shell menu.
-  Patch by Roger Serwy, updated by Bayard Randel.
+- bpo-15308: Add 'interrupt execution' (^C) to Shell menu. Patch by Roger
+  Serwy, updated by Bayard Randel.
 
-- Issue #27922: Stop IDLE tests from 'flashing' gui widgets on the screen.
+- bpo-27922: Stop IDLE tests from 'flashing' gui widgets on the screen.
 
 - Add version to title of IDLE help window.
 
-- Issue #25564: In section on IDLE -- console differences, mention that
-  using exec means that __builtins__ is defined for each statement.
+- bpo-25564: In section on IDLE -- console differences, mention that using
+  exec means that __builtins__ is defined for each statement.
 
-- Issue #27714: text_textview and test_autocomplete now pass when re-run
-  in the same process.  This occurs when test_idle fails when run with the
-  -w option but without -jn.  Fix warning from test_config.
+- bpo-27714: text_textview and test_autocomplete now pass when re-run in the
+  same process.  This occurs when test_idle fails when run with the -w
+  option but without -jn.  Fix warning from test_config.
 
-- Issue #25507: IDLE no longer runs buggy code because of its tkinter imports.
+- bpo-25507: IDLE no longer runs buggy code because of its tkinter imports.
   Users must include the same imports required to run directly in Python.
 
-- Issue #27452: add line counter and crc to IDLE configHandler test dump.
+- bpo-27452: add line counter and crc to IDLE configHandler test dump.
 
-- Issue #27365: Allow non-ascii chars in IDLE NEWS.txt, for contributor names.
+- bpo-27365: Allow non-ascii chars in IDLE NEWS.txt, for contributor names.
 
-- Issue #27245: IDLE: Cleanly delete custom themes and key bindings.
+- bpo-27245: IDLE: Cleanly delete custom themes and key bindings.
   Previously, when IDLE was started from a console or by import, a cascade
-  of warnings was emitted.  Patch by Serhiy Storchaka.
+  of warnings was emitted. Patch by Serhiy Storchaka.
 
 C API
 -----
 
-- Issue #28808: PyUnicode_CompareWithASCIIString() now never raises exceptions.
+- bpo-28808: PyUnicode_CompareWithASCIIString() now never raises exceptions.
 
-- Issue #26754: PyUnicode_FSDecoder() accepted a filename argument encoded as
-  an iterable of integers. Now only strings and bytes-like objects are accepted.
+- bpo-26754: PyUnicode_FSDecoder() accepted a filename argument encoded as
+  an iterable of integers. Now only strings and bytes-like objects are
+  accepted.
 
 Documentation
 -------------
 
-- Issue #28513: Documented command-line interface of zipfile.
+- bpo-28513: Documented command-line interface of zipfile.
 
 Tests
 -----
 
-- Issue #28950: Disallow -j0 to be combined with -T/-l/-M in regrtest
-  command line arguments.
+- bpo-28950: Disallow -j0 to be combined with -T/-l/-M in regrtest command
+  line arguments.
 
-- Issue #28666: Now test.support.rmtree is able to remove unwritable or
+- bpo-28666: Now test.support.rmtree is able to remove unwritable or
   unreadable directories.
 
-- Issue #23839: Various caches now are cleared before running every test file.
+- bpo-23839: Various caches now are cleared before running every test file.
 
-- Issue #28409: regrtest: fix the parser of command line arguments.
+- bpo-28409: regrtest: fix the parser of command line arguments.
 
-- Issue #27787: Call gc.collect() before checking each test for "dangling
+- bpo-27787: Call gc.collect() before checking each test for "dangling
   threads", since the dangling threads are weak references.
 
-- Issue #27369: In test_pyexpat, avoid testing an error message detail that
+- bpo-27369: In test_pyexpat, avoid testing an error message detail that
   changed in Expat 2.2.0.
 
 Tools/Demos
 -----------
 
-- Issue #27952: Get Tools/scripts/fixcid.py working with Python 3 and the
+- bpo-27952: Get Tools/scripts/fixcid.py working with Python 3 and the
   current "re" module, avoid invalid Python backslash escapes, and fix a bug
   parsing escaped C quote signs.
 
-- Issue #27332: Fixed the type of the first argument of module-level functions
+- bpo-27332: Fixed the type of the first argument of module-level functions
   generated by Argument Clinic.  Patch by Petr Viktorin.
 
-- Issue #27418: Fixed Tools/importbench/importbench.py.
+- bpo-27418: Fixed Tools/importbench/importbench.py.
 
 Windows
 -------
 
-- Issue #28251: Improvements to help manuals on Windows.
+- bpo-28251: Improvements to help manuals on Windows.
 
-- Issue #28110: launcher.msi has different product codes between 32-bit and
+- bpo-28110: launcher.msi has different product codes between 32-bit and
   64-bit
 
-- Issue #25144: Ensures TargetDir is set before continuing with custom
-  install.
+- bpo-25144: Ensures TargetDir is set before continuing with custom install.
 
-- Issue #27469: Adds a shell extension to the launcher so that drag and drop
+- bpo-27469: Adds a shell extension to the launcher so that drag and drop
   works correctly.
 
-- Issue #27309: Enabled proper Windows styles in python[w].exe manifest.
+- bpo-27309: Enabled proper Windows styles in python[w].exe manifest.
 
 Build
 -----
 
-- Issue #29080: Removes hard dependency on hg.exe from PCBuild/build.bat
+- bpo-29080: Removes hard dependency on hg.exe from PCBuild/build.bat
 
-- Issue #23903: Added missed names to PC/python3.def.
+- bpo-23903: Added missed names to PC/python3.def.
 
-- Issue #10656: Fix out-of-tree building on AIX.  Patch by Tristan Carel and
+- bpo-10656: Fix out-of-tree building on AIX.  Patch by Tristan Carel and
   Michael Haubenwallner.
 
-- Issue #26359: Rename --with-optimiations to --enable-optimizations.
+- bpo-26359: Rename --with-optimiations to --enable-optimizations.
 
-- Issue #28444: Fix missing extensions modules when cross compiling.
+- bpo-28444: Fix missing extensions modules when cross compiling.
 
-- Issue #28248: Update Windows build and OS X installers to use OpenSSL 1.0.2j.
+- bpo-28248: Update Windows build and OS X installers to use OpenSSL 1.0.2j.
 
-- Issue #28258: Fixed build with Estonian locale (python-config and distclean
+- bpo-28258: Fixed build with Estonian locale (python-config and distclean
   targets in Makefile).  Patch by Arfrever Frehtes Taifersar Arahesis.
 
-- Issue #26661: setup.py now detects system libffi with multiarch wrapper.
+- bpo-26661: setup.py now detects system libffi with multiarch wrapper.
 
-- Issue #28066: Fix the logic that searches build directories for generated
+- bpo-28066: Fix the logic that searches build directories for generated
   include files when building outside the source tree.
 
-- Issue #15819: Remove redundant include search directory option for building
+- bpo-15819: Remove redundant include search directory option for building
   outside the source tree.
 
-- Issue #27566: Fix clean target in freeze makefile (patch by Lisa Roach)
+- bpo-27566: Fix clean target in freeze makefile (patch by Lisa Roach)
 
-- Issue #27705: Update message in validate_ucrtbase.py
+- bpo-27705: Update message in validate_ucrtbase.py
 
-- Issue #27983: Cause lack of llvm-profdata tool when using clang as
-  required for PGO linking to be a configure time error rather than
-  make time when --with-optimizations is enabled.  Also improve our
-  ability to find the llvm-profdata tool on MacOS and some Linuxes.
+- bpo-27983: Cause lack of llvm-profdata tool when using clang as required
+  for PGO linking to be a configure time error rather than make time when
+  --with- optimizations is enabled.  Also improve our ability to find the
+  llvm- profdata tool on MacOS and some Linuxes.
 
-- Issue #26307: The profile-opt build now applies PGO to the built-in modules.
+- bpo-26307: The profile-opt build now applies PGO to the built-in modules.
 
-- Issue #26359: Add the --with-optimizations configure flag.
+- bpo-26359: Add the --with-optimizations configure flag.
 
-- Issue #27713: Suppress spurious build warnings when updating importlib's
-  bootstrap files.  Patch by Xiang Zhang
+- bpo-27713: Suppress spurious build warnings when updating importlib's
+  bootstrap files. Patch by Xiang Zhang
 
-- Issue #25825: Correct the references to Modules/python.exp and ld_so_aix,
-  which are required on AIX.  This updates references to an installation path
-  that was changed in 3.2a4, and undoes changed references to the build tree
-  that were made in 3.5.0a1.
+- bpo-25825: Correct the references to Modules/python.exp and ld_so_aix,
+  which are required on AIX.  This updates references to an installation
+  path that was changed in 3.2a4, and undoes changed references to the build
+  tree that were made in 3.5.0a1.
 
-- Issue #27453: CPP invocation in configure must use CPPFLAGS. Patch by
-  Chi Hsuan Yen.
+- bpo-27453: CPP invocation in configure must use CPPFLAGS. Patch by Chi
+  Hsuan Yen.
 
-- Issue #27641: The configure script now inserts comments into the makefile
-  to prevent the pgen and _freeze_importlib executables from being cross-
+- bpo-27641: The configure script now inserts comments into the makefile to
+  prevent the pgen and _freeze_importlib executables from being cross-
   compiled.
 
-- Issue #26662: Set PYTHON_FOR_GEN in configure as the Python program to be
+- bpo-26662: Set PYTHON_FOR_GEN in configure as the Python program to be
   used for file generation during the build.
 
-- Issue #10910: Avoid C++ compilation errors on FreeBSD and OS X.
-  Also update FreedBSD version checks for the original ctype UTF-8 workaround.
+- bpo-10910: Avoid C++ compilation errors on FreeBSD and OS X. Also update
+  FreedBSD version checks for the original ctype UTF-8 workaround.
 
-- Issue #28676: Prevent missing 'getentropy' declaration warning on macOS.
+- bpo-28676: Prevent missing 'getentropy' declaration warning on macOS.
   Patch by Gareth Rees.
 
 
-What's New in Python 3.5.2?
-===========================
+What's New in Python 3.5.2 final?
+=================================
 
-Release date: 2016-06-26
+*Release date: 2016-06-26*
 
 Core and Builtins
 -----------------
 
-- Issue #26930: Update Windows builds to use OpenSSL 1.0.2h.
+- bpo-26930: Update Windows builds to use OpenSSL 1.0.2h.
 
 Tests
 -----
 
-- Issue #26867: Ubuntu's openssl OP_NO_SSLv3 is forced on by default; fix test.
+- bpo-26867: Ubuntu's openssl OP_NO_SSLv3 is forced on by default; fix test.
 
 IDLE
 ----
 
-- Issue #27365: Allow non-ascii in idlelib/NEWS.txt - minimal part for 3.5.2.
+- bpo-27365: Allow non-ascii in idlelib/NEWS.txt - minimal part for 3.5.2.
 
 
 What's New in Python 3.5.2 release candidate 1?
 ===============================================
 
-Release date: 2016-06-12
+*Release date: 2016-06-12*
 
 Core and Builtins
 -----------------
 
-- Issue #27066: Fixed SystemError if a custom opener (for open()) returns a
+- bpo-27066: Fixed SystemError if a custom opener (for open()) returns a
   negative number without setting an exception.
 
-- Issue #20041: Fixed TypeError when frame.f_trace is set to None.
-  Patch by Xavier de Gaye.
+- bpo-20041: Fixed TypeError when frame.f_trace is set to None. Patch by
+  Xavier de Gaye.
 
-- Issue #26168: Fixed possible refleaks in failing Py_BuildValue() with the "N"
+- bpo-26168: Fixed possible refleaks in failing Py_BuildValue() with the "N"
   format unit.
 
-- Issue #26991: Fix possible refleak when creating a function with annotations.
+- bpo-26991: Fix possible refleak when creating a function with annotations.
 
-- Issue #27039: Fixed bytearray.remove() for values greater than 127.  Patch by
+- bpo-27039: Fixed bytearray.remove() for values greater than 127.  Patch by
   Joe Jevnik.
 
-- Issue #23640: int.from_bytes() no longer bypasses constructors for subclasses.
+- bpo-23640: int.from_bytes() no longer bypasses constructors for
+  subclasses.
 
-- Issue #26811: gc.get_objects() no longer contains a broken tuple with NULL
+- bpo-26811: gc.get_objects() no longer contains a broken tuple with NULL
   pointer.
 
-- Issue #20120: Use RawConfigParser for .pypirc parsing,
-  removing support for interpolation unintentionally added
-  with move to Python 3. Behavior no longer does any
-  interpolation in .pypirc files, matching behavior in Python
-  2.7 and Setuptools 19.0.
+- bpo-20120: Use RawConfigParser for .pypirc parsing, removing support for
+  interpolation unintentionally added with move to Python 3. Behavior no
+  longer does any interpolation in .pypirc files, matching behavior in
+  Python 2.7 and Setuptools 19.0.
 
-- Issue #26659: Make the builtin slice type support cycle collection.
+- bpo-26659: Make the builtin slice type support cycle collection.
 
-- Issue #26718: super.__init__ no longer leaks memory if called multiple times.
+- bpo-26718: super.__init__ no longer leaks memory if called multiple times.
   NOTE: A direct call of super.__init__ is not endorsed!
 
-- Issue #25339: PYTHONIOENCODING now has priority over locale in setting the
+- bpo-25339: PYTHONIOENCODING now has priority over locale in setting the
   error handler for stdin and stdout.
 
-- Issue #26494: Fixed crash on iterating exhausting iterators.
-  Affected classes are generic sequence iterators, iterators of str, bytes,
-  bytearray, list, tuple, set, frozenset, dict, OrderedDict, corresponding
-  views and os.scandir() iterator.
+- bpo-26494: Fixed crash on iterating exhausting iterators. Affected classes
+  are generic sequence iterators, iterators of str, bytes, bytearray, list,
+  tuple, set, frozenset, dict, OrderedDict, corresponding views and
+  os.scandir() iterator.
 
-- Issue #26581: If coding cookie is specified multiple times on a line in
+- bpo-26581: If coding cookie is specified multiple times on a line in
   Python source code file, only the first one is taken to account.
 
-- Issue #26464: Fix str.translate() when string is ASCII and first replacements
+- bpo-26464: Fix str.translate() when string is ASCII and first replacements
   removes character, but next replacement uses a non-ASCII character or a
   string longer than 1 character. Regression introduced in Python 3.5.0.
 
-- Issue #22836: Ensure exception reports from PyErr_Display() and
+- bpo-22836: Ensure exception reports from PyErr_Display() and
   PyErr_WriteUnraisable() are sensible even when formatting them produces
   secondary errors.  This affects the reports produced by
   sys.__excepthook__() and when __del__() raises an exception.
 
-- Issue #26302: Correct behavior to reject comma as a legal character for
+- bpo-26302: Correct behavior to reject comma as a legal character for
   cookie names.
 
-- Issue #4806: Avoid masking the original TypeError exception when using star
-  (``*``) unpacking in function calls.  Based on patch by Hagen Fürstenau and
-  Daniel Urban.
+- bpo-4806: Avoid masking the original TypeError exception when using star
+  (``*``) unpacking in function calls.  Based on patch by Hagen Fürstenau
+  and Daniel Urban.
 
-- Issue #27138: Fix the doc comment for FileFinder.find_spec().
+- bpo-27138: Fix the doc comment for FileFinder.find_spec().
 
-- Issue #26154: Add a new private _PyThreadState_UncheckedGet() function to get
-  the current Python thread state, but don't issue a fatal error if it is NULL.
-  This new function must be used instead of accessing directly the
+- bpo-26154: Add a new private _PyThreadState_UncheckedGet() function to get
+  the current Python thread state, but don't issue a fatal error if it is
+  NULL. This new function must be used instead of accessing directly the
   _PyThreadState_Current variable.  The variable is no more exposed since
   Python 3.5.1 to hide the exact implementation of atomic C types, to avoid
   compiler issues.
 
-- Issue #26194:  Deque.insert() gave odd results for bounded deques that had
-  reached their maximum size.  Now an IndexError will be raised when attempting
-  to insert into a full deque.
+- bpo-26194: Deque.insert() gave odd results for bounded deques that had
+  reached their maximum size.  Now an IndexError will be raised when
+  attempting to insert into a full deque.
 
-- Issue #25843: When compiling code, don't merge constants if they are equal
-  but have a different types. For example, ``f1, f2 = lambda: 1, lambda: 1.0``
-  is now correctly compiled to two different functions: ``f1()`` returns ``1``
-  (``int``) and ``f2()`` returns ``1.0`` (``int``), even if ``1`` and ``1.0``
-  are equal.
+- bpo-25843: When compiling code, don't merge constants if they are equal
+  but have a different types. For example, ``f1, f2 = lambda: 1, lambda:
+  1.0`` is now correctly compiled to two different functions: ``f1()``
+  returns ``1`` (``int``) and ``f2()`` returns ``1.0`` (``int``), even if
+  ``1`` and ``1.0`` are equal.
 
-- Issue #22995: [UPDATE] Comment out the one of the pickleability tests in
+- bpo-22995: [UPDATE] Comment out the one of the pickleability tests in
   _PyObject_GetState() due to regressions observed in Cython-based projects.
 
-- Issue #25961: Disallowed null characters in the type name.
+- bpo-25961: Disallowed null characters in the type name.
 
-- Issue #25973: Fix segfault when an invalid nonlocal statement binds a name
+- bpo-25973: Fix segfault when an invalid nonlocal statement binds a name
   starting with two underscores.
 
-- Issue #22995: Instances of extension types with a state that aren't
+- bpo-22995: Instances of extension types with a state that aren't
   subclasses of list or dict and haven't implemented any pickle-related
-  methods (__reduce__, __reduce_ex__, __getnewargs__, __getnewargs_ex__,
-  or __getstate__), can no longer be pickled.  Including memoryview.
+  methods (__reduce__, __reduce_ex__, __getnewargs__, __getnewargs_ex__, or
+  __getstate__), can no longer be pickled.  Including memoryview.
 
-- Issue #20440: Massive replacing unsafe attribute setting code with special
+- bpo-20440: Massive replacing unsafe attribute setting code with special
   macro Py_SETREF.
 
-- Issue #25766: Special method __bytes__() now works in str subclasses.
+- bpo-25766: Special method __bytes__() now works in str subclasses.
 
-- Issue #25421: __sizeof__ methods of builtin types now use dynamic basic size.
+- bpo-25421: __sizeof__ methods of builtin types now use dynamic basic size.
   This allows sys.getsize() to work correctly with their subclasses with
   __slots__ defined.
 
-- Issue #25709: Fixed problem with in-place string concatenation and utf-8
+- bpo-25709: Fixed problem with in-place string concatenation and utf-8
   cache.
 
-- Issue #27147: Mention PEP 420 in the importlib docs.
+- bpo-27147: Mention PEP 420 in the importlib docs.
 
-- Issue #24097: Fixed crash in object.__reduce__() if slot name is freed inside
+- bpo-24097: Fixed crash in object.__reduce__() if slot name is freed inside
   __getattr__.
 
-- Issue #24731: Fixed crash on converting objects with special methods
+- bpo-24731: Fixed crash on converting objects with special methods
   __bytes__, __trunc__, and __float__ returning instances of subclasses of
-  bytes, int, and float to subclasses of bytes, int, and float correspondingly.
+  bytes, int, and float to subclasses of bytes, int, and float
+  correspondingly.
 
-- Issue #26478: Fix semantic bugs when using binary operators with dictionary
+- bpo-26478: Fix semantic bugs when using binary operators with dictionary
   views and tuples.
 
-- Issue #26171: Fix possible integer overflow and heap corruption in
+- bpo-26171: Fix possible integer overflow and heap corruption in
   zipimporter.get_data().
 
-- Issue #25660: Fix TAB key behaviour in REPL with readline.
+- bpo-25660: Fix TAB key behaviour in REPL with readline.
 
-- Issue #25887: Raise a RuntimeError when a coroutine object is awaited
-  more than once.
+- bpo-25887: Raise a RuntimeError when a coroutine object is awaited more
+  than once.
 
-- Issue #27243: Update the __aiter__ protocol: instead of returning
-  an awaitable that resolves to an asynchronous iterator, the asynchronous
+- bpo-27243: Update the __aiter__ protocol: instead of returning an
+  awaitable that resolves to an asynchronous iterator, the asynchronous
   iterator should be returned directly.  Doing the former will trigger a
   PendingDeprecationWarning.
 
+Security
+--------
 
-Library
--------
+- bpo-26556: Update expat to 2.1.1, fixes CVE-2015-1283.
 
-- [Security] Issue #26556: Update expat to 2.1.1, fixes CVE-2015-1283.
+- Fix TLS stripping vulnerability in smtplib, CVE-2016-0772. Reported by
+  Team Oststrom
 
-- [Security] Fix TLS stripping vulnerability in smtplib, CVE-2016-0772.
-  Reported by Team Oststrom
+Library
+-------
 
-- Issue #21386: Implement missing IPv4Address.is_global property.  It was
+- bpo-21386: Implement missing IPv4Address.is_global property.  It was
   documented since 07a5610bae9d.  Initial patch by Roger Luethi.
 
-- Issue #20900: distutils register command now decodes HTTP responses
+- bpo-20900: distutils register command now decodes HTTP responses
   correctly.  Initial patch by ingrid.
 
-- A new version of typing.py provides several new classes and
-  features: @overload outside stubs, Reversible, DefaultDict, Text,
-  ContextManager, Type[], NewType(), TYPE_CHECKING, and numerous bug
-  fixes (note that some of the new features are not yet implemented in
-  mypy or other static analyzers).  Also classes for PEP 492
-  (Awaitable, AsyncIterable, AsyncIterator) have been added (in fact
-  they made it into 3.5.1 but were never mentioned).
+- A new version of typing.py provides several new classes and features:
+  @overload outside stubs, Reversible, DefaultDict, Text, ContextManager,
+  Type[], NewType(), TYPE_CHECKING, and numerous bug fixes (note that some
+  of the new features are not yet implemented in mypy or other static
+  analyzers). Also classes for PEP 492 (Awaitable, AsyncIterable,
+  AsyncIterator) have been added (in fact they made it into 3.5.1 but were
+  never mentioned).
 
-- Issue #25738: Stop http.server.BaseHTTPRequestHandler.send_error() from
+- bpo-25738: Stop http.server.BaseHTTPRequestHandler.send_error() from
   sending a message body for 205 Reset Content.  Also, don't send Content
   header fields in responses that don't have a body.  Patch by Susumu
   Koshiba.
 
-- Issue #21313: Fix the "platform" module to tolerate when sys.version
-  contains truncated build information.
+- bpo-21313: Fix the "platform" module to tolerate when sys.version contains
+  truncated build information.
+
+Security
+--------
 
-- [Security] Issue #26839: On Linux, :func:`os.urandom` now calls
-  ``getrandom()`` with ``GRND_NONBLOCK`` to fall back on reading
-  ``/dev/urandom`` if the urandom entropy pool is not initialized yet. Patch
-  written by Colm Buckley.
+- bpo-26839: On Linux, :func:`os.urandom` now calls ``getrandom()`` with
+  ``GRND_NONBLOCK`` to fall back on reading ``/dev/urandom`` if the urandom
+  entropy pool is not initialized yet. Patch written by Colm Buckley.
+
+Library
+-------
 
-- Issue #27164: In the zlib module, allow decompressing raw Deflate streams
+- bpo-27164: In the zlib module, allow decompressing raw Deflate streams
   with a predefined zdict.  Based on patch by Xiang Zhang.
 
-- Issue #24291: Fix wsgiref.simple_server.WSGIRequestHandler to completely
+- bpo-24291: Fix wsgiref.simple_server.WSGIRequestHandler to completely
   write data to the client.  Previously it could do partial writes and
   truncate data.  Also, wsgiref.handler.ServerHandler can now handle stdout
   doing partial writes, but this is deprecated.
 
-- Issue #26809: Add ``__all__`` to :mod:`string`.  Patch by Emanuel Barry.
+- bpo-26809: Add ``__all__`` to :mod:`string`.  Patch by Emanuel Barry.
 
-- Issue #26373: subprocess.Popen.communicate now correctly ignores
-  BrokenPipeError when the child process dies before .communicate()
-  is called in more/all circumstances.
+- bpo-26373: subprocess.Popen.communicate now correctly ignores
+  BrokenPipeError when the child process dies before .communicate() is
+  called in more/all circumstances.
 
-- Issue #21776: distutils.upload now correctly handles HTTPError.
-  Initial patch by Claudiu Popa.
+- bpo-21776: distutils.upload now correctly handles HTTPError. Initial patch
+  by Claudiu Popa.
 
-- Issue #27114: Fix SSLContext._load_windows_store_certs fails with
+- bpo-27114: Fix SSLContext._load_windows_store_certs fails with
   PermissionError
 
-- Issue #18383: Avoid creating duplicate filters when using filterwarnings
-  and simplefilter.  Based on patch by Alex Shkop.
+- bpo-18383: Avoid creating duplicate filters when using filterwarnings and
+  simplefilter. Based on patch by Alex Shkop.
 
-- Issue #27057: Fix os.set_inheritable() on Android, ioctl() is blocked by
+- bpo-27057: Fix os.set_inheritable() on Android, ioctl() is blocked by
   SELinux and fails with EACCESS. The function now falls back to fcntl().
   Patch written by Michał Bednarski.
 
-- Issue #27014: Fix infinite recursion using typing.py.  Thanks to Kalle Tuure!
+- bpo-27014: Fix infinite recursion using typing.py.  Thanks to Kalle Tuure!
 
-- Issue #14132: Fix urllib.request redirect handling when the target only has
-  query string.  Original fix by Ján Janech.
+- bpo-14132: Fix urllib.request redirect handling when the target only has a
+  query string.  Original fix by Ján Janech.
 
-- Issue #17214: The "urllib.request" module now percent-encodes non-ASCII
-  bytes found in redirect target URLs.  Some servers send Location header
-  fields with non-ASCII bytes, but "http.client" requires the request target
-  to be ASCII-encodable, otherwise a UnicodeEncodeError is raised.  Based on
+- bpo-17214: The "urllib.request" module now percent-encodes non-ASCII bytes
+  found in redirect target URLs.  Some servers send Location header fields
+  with non- ASCII bytes, but "http.client" requires the request target to be
+  ASCII- encodable, otherwise a UnicodeEncodeError is raised.  Based on
   patch by Christian Heimes.
 
-- Issue #26892: Honor debuglevel flag in urllib.request.HTTPHandler. Patch
+- bpo-26892: Honor debuglevel flag in urllib.request.HTTPHandler. Patch
   contributed by Chi Hsuan Yen.
 
-- Issue #22274: In the subprocess module, allow stderr to be redirected to
+- bpo-22274: In the subprocess module, allow stderr to be redirected to
   stdout even when stdout is not redirected.  Patch by Akira Li.
 
-- Issue #26807: mock_open 'files' no longer error on readline at end of file.
+- bpo-26807: mock_open 'files' no longer error on readline at end of file.
   Patch from Yolanda Robla.
 
-- Issue #25745: Fixed leaking a userptr in curses panel destructor.
+- bpo-25745: Fixed leaking a userptr in curses panel destructor.
 
-- Issue #26977: Removed unnecessary, and ignored, call to sum of squares helper
+- bpo-26977: Removed unnecessary, and ignored, call to sum of squares helper
   in statistics.pvariance.
 
-- Issue #26881: The modulefinder module now supports extended opcode arguments.
+- bpo-26881: The modulefinder module now supports extended opcode arguments.
 
-- Issue #23815: Fixed crashes related to directly created instances of types in
+- bpo-23815: Fixed crashes related to directly created instances of types in
   _tkinter and curses.panel modules.
 
-- Issue #17765: weakref.ref() no longer silently ignores keyword arguments.
+- bpo-17765: weakref.ref() no longer silently ignores keyword arguments.
   Patch by Georg Brandl.
 
-- Issue #26873: xmlrpc now raises ResponseError on unsupported type tags
+- bpo-26873: xmlrpc now raises ResponseError on unsupported type tags
   instead of silently return incorrect result.
 
-- Issue #26711: Fixed the comparison of plistlib.Data with other types.
+- bpo-26711: Fixed the comparison of plistlib.Data with other types.
 
-- Issue #24114: Fix an uninitialized variable in `ctypes.util`.
+- bpo-24114: Fix an uninitialized variable in `ctypes.util`.
 
-  The bug only occurs on SunOS when the ctypes implementation searches
-  for the `crle` program.  Patch by Xiang Zhang.  Tested on SunOS by
-  Kees Bos.
+  The bug only occurs on SunOS when the ctypes implementation searches for
+  the `crle` program.  Patch by Xiang Zhang.  Tested on SunOS by Kees Bos.
 
-- Issue #26864: In urllib.request, change the proxy bypass host checking
+- bpo-26864: In urllib.request, change the proxy bypass host checking
   against no_proxy to be case-insensitive, and to not match unrelated host
   names that happen to have a bypassed hostname as a suffix.  Patch by Xiang
   Zhang.
 
-- Issue #26634: recursive_repr() now sets __qualname__ of wrapper.  Patch by
+- bpo-26634: recursive_repr() now sets __qualname__ of wrapper.  Patch by
   Xiang Zhang.
 
-- Issue #26804: urllib.request will prefer lower_case proxy environment
-  variables over UPPER_CASE or Mixed_Case ones. Patch contributed by Hans-Peter
-  Jansen.
+- bpo-26804: urllib.request will prefer lower_case proxy environment
+  variables over UPPER_CASE or Mixed_Case ones. Patch contributed by Hans-
+  Peter Jansen.
 
-- Issue #26837: assertSequenceEqual() now correctly outputs non-stringified
-  differing items (like bytes in the -b mode).  This affects assertListEqual()
-  and assertTupleEqual().
+- bpo-26837: assertSequenceEqual() now correctly outputs non-stringified
+  differing items (like bytes in the -b mode).  This affects
+  assertListEqual() and assertTupleEqual().
 
-- Issue #26041: Remove "will be removed in Python 3.7" from deprecation
-  messages of platform.dist() and platform.linux_distribution().
-  Patch by Kumaripaba Miyurusara Athukorala.
+- bpo-26041: Remove "will be removed in Python 3.7" from deprecation
+  messages of platform.dist() and platform.linux_distribution(). Patch by
+  Kumaripaba Miyurusara Athukorala.
 
-- Issue #26822: itemgetter, attrgetter and methodcaller objects no longer
+- bpo-26822: itemgetter, attrgetter and methodcaller objects no longer
   silently ignore keyword arguments.
 
-- Issue #26733: Disassembling a class now disassembles class and static methods.
-  Patch by Xiang Zhang.
+- bpo-26733: Disassembling a class now disassembles class and static
+  methods. Patch by Xiang Zhang.
 
-- Issue #26801: Fix error handling in :func:`shutil.get_terminal_size`, catch
-  :exc:`AttributeError` instead of :exc:`NameError`. Patch written by Emanuel
-  Barry.
+- bpo-26801: Fix error handling in :func:`shutil.get_terminal_size`, catch
+  :exc:`AttributeError` instead of :exc:`NameError`. Patch written by
+  Emanuel Barry.
 
-- Issue #24838: tarfile's ustar and gnu formats now correctly calculate name
+- bpo-24838: tarfile's ustar and gnu formats now correctly calculate name
   and link field limits for multibyte character encodings like utf-8.
 
-- [Security] Issue #26657: Fix directory traversal vulnerability with
-  http.server on Windows.  This fixes a regression that was introduced in
-  3.3.4rc1 and 3.4.0rc1.  Based on patch by Philipp Hagemeister.
+Security
+--------
+
+- bpo-26657: Fix directory traversal vulnerability with http.server on
+  Windows.  This fixes a regression that was introduced in 3.3.4rc1 and
+  3.4.0rc1.  Based on patch by Philipp Hagemeister.
+
+Library
+-------
 
-- Issue #26717: Stop encoding Latin-1-ized WSGI paths with UTF-8.  Patch by
+- bpo-26717: Stop encoding Latin-1-ized WSGI paths with UTF-8.  Patch by
   Anthony Sottile.
 
-- Issue #26735: Fix :func:`os.urandom` on Solaris 11.3 and newer when reading
+- bpo-26735: Fix :func:`os.urandom` on Solaris 11.3 and newer when reading
   more than 1,024 bytes: call ``getrandom()`` multiple times with a limit of
   1024 bytes per call.
 
-- Issue #16329: Add .webm to mimetypes.types_map.  Patch by Giampaolo Rodola'.
+- bpo-16329: Add .webm to mimetypes.types_map.  Patch by Giampaolo Rodola'.
 
-- Issue #13952: Add .csv to mimetypes.types_map.  Patch by Geoff Wilson.
+- bpo-13952: Add .csv to mimetypes.types_map.  Patch by Geoff Wilson.
 
-- Issue #26709: Fixed Y2038 problem in loading binary PLists.
+- bpo-26709: Fixed Y2038 problem in loading binary PLists.
 
-- Issue #23735: Handle terminal resizing with Readline 6.3+ by installing our
+- bpo-23735: Handle terminal resizing with Readline 6.3+ by installing our
   own SIGWINCH handler.  Patch by Eric Price.
 
-- Issue #26586: In http.server, respond with "413 Request header fields too
+- bpo-26586: In http.server, respond with "413 Request header fields too
   large" if there are too many header fields to parse, rather than killing
   the connection and raising an unhandled exception.  Patch by Xiang Zhang.
 
-- Issue #22854: Change BufferedReader.writable() and
-  BufferedWriter.readable() to always return False.
+- bpo-22854: Change BufferedReader.writable() and BufferedWriter.readable()
+  to always return False.
 
-- Issue #25195: Fix a regression in mock.MagicMock. _Call is a subclass of
+- bpo-25195: Fix a regression in mock.MagicMock. _Call is a subclass of
   tuple (changeset 3603bae63c13 only works for classes) so we need to
   implement __ne__ ourselves.  Patch by Andrew Plummer.
 
-- Issue #26644: Raise ValueError rather than SystemError when a negative
-  length is passed to SSLSocket.recv() or read().
+- bpo-26644: Raise ValueError rather than SystemError when a negative length
+  is passed to SSLSocket.recv() or read().
 
-- Issue #23804: Fix SSL recv(0) and read(0) methods to return zero bytes
+- bpo-23804: Fix SSL recv(0) and read(0) methods to return zero bytes
   instead of up to 1024.
 
-- Issue #26616: Fixed a bug in datetime.astimezone() method.
+- bpo-26616: Fixed a bug in datetime.astimezone() method.
 
-- Issue #21925: :func:`warnings.formatwarning` now catches exceptions on
-  ``linecache.getline(...)`` to be able to log :exc:`ResourceWarning` emitted
-  late during the Python shutdown process.
+- bpo-21925: :func:`warnings.formatwarning` now catches exceptions on
+  ``linecache.getline(...)`` to be able to log :exc:`ResourceWarning`
+  emitted late during the Python shutdown process.
 
-- Issue #24266: Ctrl+C during Readline history search now cancels the search
+- bpo-24266: Ctrl+C during Readline history search now cancels the search
   mode when compiled with Readline 7.
 
-- Issue #26560: Avoid potential ValueError in BaseHandler.start_response.
+- bpo-26560: Avoid potential ValueError in BaseHandler.start_response.
   Initial patch by Peter Inglesby.
 
-- [Security] Issue #26313: ssl.py _load_windows_store_certs fails if windows
-  cert store is empty. Patch by Baji.
+Security
+--------
 
-- Issue #26569: Fix :func:`pyclbr.readmodule` and :func:`pyclbr.readmodule_ex`
+- bpo-26313: ssl.py _load_windows_store_certs fails if windows cert store is
+  empty. Patch by Baji.
+
+Library
+-------
+
+- bpo-26569: Fix :func:`pyclbr.readmodule` and :func:`pyclbr.readmodule_ex`
   to support importing packages.
 
-- Issue #26499: Account for remaining Content-Length in
-  HTTPResponse.readline() and read1().  Based on patch by Silent Ghost.
-  Also document that HTTPResponse now supports these methods.
+- bpo-26499: Account for remaining Content-Length in HTTPResponse.readline()
+  and read1(). Based on patch by Silent Ghost. Also document that
+  HTTPResponse now supports these methods.
 
-- Issue #25320: Handle sockets in directories unittest discovery is scanning.
+- bpo-25320: Handle sockets in directories unittest discovery is scanning.
   Patch from Victor van den Elzen.
 
-- Issue #16181: cookiejar.http2time() now returns None if year is higher than
+- bpo-16181: cookiejar.http2time() now returns None if year is higher than
   datetime.MAXYEAR.
 
-- Issue #26513: Fixes platform module detection of Windows Server
+- bpo-26513: Fixes platform module detection of Windows Server
 
-- Issue #23718: Fixed parsing time in week 0 before Jan 1.  Original patch by
+- bpo-23718: Fixed parsing time in week 0 before Jan 1.  Original patch by
   Tamás Bence Gedai.
 
-- Issue #20589: Invoking Path.owner() and Path.group() on Windows now raise
+- bpo-20589: Invoking Path.owner() and Path.group() on Windows now raise
   NotImplementedError instead of ImportError.
 
-- Issue #26177: Fixed the keys() method for Canvas and Scrollbar widgets.
+- bpo-26177: Fixed the keys() method for Canvas and Scrollbar widgets.
 
-- Issue #15068: Got rid of excessive buffering in the fileinput module.
-  The bufsize parameter is no longer used.
+- bpo-15068: Got rid of excessive buffering in the fileinput module. The
+  bufsize parameter is no longer used.
 
-- Issue #2202: Fix UnboundLocalError in
-  AbstractDigestAuthHandler.get_algorithm_impls.  Initial patch by Mathieu
+- bpo-2202: Fix UnboundLocalError in
+  AbstractDigestAuthHandler.get_algorithm_impls. Initial patch by Mathieu
   Dupuy.
 
-- Issue #25718: Fixed pickling and copying the accumulate() iterator with
-  total is None.
+- bpo-25718: Fixed pickling and copying the accumulate() iterator with total
+  is None.
 
-- Issue #26475: Fixed debugging output for regular expressions with the (?x)
+- bpo-26475: Fixed debugging output for regular expressions with the (?x)
   flag.
 
-- Issue #26457: Fixed the subnets() methods in IP network classes for the case
-  when resulting prefix length is equal to maximal prefix length.
-  Based on patch by Xiang Zhang.
+- bpo-26457: Fixed the subnets() methods in IP network classes for the case
+  when resulting prefix length is equal to maximal prefix length. Based on
+  patch by Xiang Zhang.
 
-- Issue #26385: Remove the file if the internal open() call in
-  NamedTemporaryFile() fails.  Patch by Silent Ghost.
+- bpo-26385: Remove the file if the internal open() call in
+  NamedTemporaryFile() fails. Patch by Silent Ghost.
 
-- Issue #26402: Fix XML-RPC client to retry when the server shuts down a
+- bpo-26402: Fix XML-RPC client to retry when the server shuts down a
   persistent connection.  This was a regression related to the new
   http.client.RemoteDisconnected exception in 3.5.0a4.
 
-- Issue #25913: Leading ``<~`` is optional now in base64.a85decode() with
+- bpo-25913: Leading ``<~`` is optional now in base64.a85decode() with
   adobe=True.  Patch by Swati Jaiswal.
 
-- Issue #26186: Remove an invalid type check in importlib.util.LazyLoader.
+- bpo-26186: Remove an invalid type check in importlib.util.LazyLoader.
 
-- Issue #26367: importlib.__import__() raises SystemError like
-  builtins.__import__() when ``level`` is specified but without an accompanying
-  package specified.
+- bpo-26367: importlib.__import__() raises SystemError like
+  builtins.__import__() when ``level`` is specified but without an
+  accompanying package specified.
 
-- Issue #26309: In the "socketserver" module, shut down the request (closing
+- bpo-26309: In the "socketserver" module, shut down the request (closing
   the connected socket) when verify_request() returns false.  Patch by Aviv
   Palivoda.
 
-- [Security] Issue #25939: On Windows open the cert store readonly in
+Security
+--------
+
+- bpo-25939: On Windows open the cert store readonly in
   ssl.enum_certificates.
 
-- Issue #25995: os.walk() no longer uses FDs proportional to the tree depth.
+Library
+-------
+
+- bpo-25995: os.walk() no longer uses FDs proportional to the tree depth.
 
-- Issue #26117: The os.scandir() iterator now closes file descriptor not only
+- bpo-26117: The os.scandir() iterator now closes file descriptor not only
   when the iteration is finished, but when it was failed with error.
 
-- Issue #25911: Restored support of bytes paths in os.walk() on Windows.
+- bpo-25911: Restored support of bytes paths in os.walk() on Windows.
 
-- Issue #26045: Add UTF-8 suggestion to error message when posting a
-  non-Latin-1 string with http.client.
+- bpo-26045: Add UTF-8 suggestion to error message when posting a non-
+  Latin-1 string with http.client.
 
-- Issue #12923: Reset FancyURLopener's redirect counter even if there is an
-  exception.  Based on patches by Brian Brazil and Daniel Rocco.
+- bpo-12923: Reset FancyURLopener's redirect counter even if there is an
+  exception. Based on patches by Brian Brazil and Daniel Rocco.
 
-- Issue #25945: Fixed a crash when unpickle the functools.partial object with
-  wrong state.  Fixed a leak in failed functools.partial constructor.
-  "args" and "keywords" attributes of functools.partial have now always types
-  tuple and dict correspondingly.
+- bpo-25945: Fixed a crash when unpickle the functools.partial object with
+  wrong state. Fixed a leak in failed functools.partial constructor. "args"
+  and "keywords" attributes of functools.partial have now always types tuple
+  and dict correspondingly.
 
-- Issue #26202: copy.deepcopy() now correctly copies range() objects with
-  non-atomic attributes.
+- bpo-26202: copy.deepcopy() now correctly copies range() objects with non-
+  atomic attributes.
 
-- Issue #23076: Path.glob() now raises a ValueError if it's called with an
-  invalid pattern.  Patch by Thomas Nyberg.
+- bpo-23076: Path.glob() now raises a ValueError if it's called with an
+  invalid pattern. Patch by Thomas Nyberg.
 
-- Issue #19883: Fixed possible integer overflows in zipimport.
+- bpo-19883: Fixed possible integer overflows in zipimport.
 
-- Issue #26227: On Windows, getnameinfo(), gethostbyaddr() and
+- bpo-26227: On Windows, getnameinfo(), gethostbyaddr() and
   gethostbyname_ex() functions of the socket module now decode the hostname
   from the ANSI code page rather than UTF-8.
 
-- Issue #26147: xmlrpc now works with strings not encodable with used
-  non-UTF-8 encoding.
+- bpo-26147: xmlrpc now works with strings not encodable with used non-UTF-8
+  encoding.
 
-- Issue #25935: Garbage collector now breaks reference loops with OrderedDict.
+- bpo-25935: Garbage collector now breaks reference loops with OrderedDict.
 
-- Issue #16620: Fixed AttributeError in msilib.Directory.glob().
+- bpo-16620: Fixed AttributeError in msilib.Directory.glob().
 
-- Issue #26013: Added compatibility with broken protocol 2 pickles created
-  in old Python 3 versions (3.4.3 and lower).
+- bpo-26013: Added compatibility with broken protocol 2 pickles created in
+  old Python 3 versions (3.4.3 and lower).
 
-- Issue #25850: Use cross-compilation by default for 64-bit Windows.
+- bpo-25850: Use cross-compilation by default for 64-bit Windows.
 
-- Issue #17633: Improve zipimport's support for namespace packages.
+- bpo-17633: Improve zipimport's support for namespace packages.
 
-- Issue #24705: Fix sysconfig._parse_makefile not expanding ${} vars
-  appearing before $() vars.
+- bpo-24705: Fix sysconfig._parse_makefile not expanding ${} vars appearing
+  before $() vars.
 
-- Issue #22138: Fix mock.patch behavior when patching descriptors. Restore
+- bpo-22138: Fix mock.patch behavior when patching descriptors. Restore
   original values after patching. Patch contributed by Sean McCully.
 
-- Issue #25672: In the ssl module, enable the SSL_MODE_RELEASE_BUFFERS mode
+- bpo-25672: In the ssl module, enable the SSL_MODE_RELEASE_BUFFERS mode
   option if it is safe to do so.
 
-- Issue #26012: Don't traverse into symlinks for ``**`` pattern in
+- bpo-26012: Don't traverse into symlinks for ``**`` pattern in
   pathlib.Path.[r]glob().
 
-- Issue #24120: Ignore PermissionError when traversing a tree with
-  pathlib.Path.[r]glob().  Patch by Ulrich Petri.
+- bpo-24120: Ignore PermissionError when traversing a tree with
+  pathlib.Path.[r]glob(). Patch by Ulrich Petri.
 
-- Issue #25447: fileinput now uses sys.stdin as-is if it does not have a
-  buffer attribute (restores backward compatibility).
+- bpo-25447: fileinput now uses sys.stdin as-is if it does not have a buffer
+  attribute (restores backward compatibility).
 
-- Issue #25447: Copying the lru_cache() wrapper object now always works,
-  independedly from the type of the wrapped object (by returning the original
-  object unchanged).
+- bpo-25447: Copying the lru_cache() wrapper object now always works,
+  independedly from the type of the wrapped object (by returning the
+  original object unchanged).
 
-- Issue #24103: Fixed possible use after free in ElementTree.XMLPullParser.
+- bpo-24103: Fixed possible use after free in ElementTree.XMLPullParser.
 
-- Issue #25860: os.fwalk() no longer skips remaining directories when error
-  occurs.  Original patch by Samson Lee.
+- bpo-25860: os.fwalk() no longer skips remaining directories when error
+  occurs. Original patch by Samson Lee.
 
-- Issue #25914: Fixed and simplified OrderedDict.__sizeof__.
+- bpo-25914: Fixed and simplified OrderedDict.__sizeof__.
 
-- Issue #25902: Fixed various refcount issues in ElementTree iteration.
+- bpo-25902: Fixed various refcount issues in ElementTree iteration.
 
-- Issue #25717: Restore the previous behaviour of tolerating most fstat()
+- bpo-25717: Restore the previous behaviour of tolerating most fstat()
   errors when opening files.  This was a regression in 3.5a1, and stopped
   anonymous temporary files from working in special cases.
 
-- Issue #24903: Fix regression in number of arguments compileall accepts when
+- bpo-24903: Fix regression in number of arguments compileall accepts when
   '-d' is specified.  The check on the number of arguments has been dropped
   completely as it never worked correctly anyway.
 
-- Issue #25764: In the subprocess module, preserve any exception caused by
+- bpo-25764: In the subprocess module, preserve any exception caused by
   fork() failure when preexec_fn is used.
 
-- Issue #6478: _strptime's regexp cache now is reset after changing timezone
+- bpo-6478: _strptime's regexp cache now is reset after changing timezone
   with time.tzset().
 
-- Issue #14285: When executing a package with the "python -m package" option,
+- bpo-14285: When executing a package with the "python -m package" option,
   and package initialization fails, a proper traceback is now reported.  The
-  "runpy" module now lets exceptions from package initialization pass back to
-  the caller, rather than raising ImportError.
+  "runpy" module now lets exceptions from package initialization pass back
+  to the caller, rather than raising ImportError.
 
-- Issue #19771: Also in runpy and the "-m" option, omit the irrelevant
-  message ". . . is a package and cannot be directly executed" if the package
-  could not even be initialized (e.g. due to a bad ``*.pyc`` file).
+- bpo-19771: Also in runpy and the "-m" option, omit the irrelevant message
+  ". . . is a package and cannot be directly executed" if the package could
+  not even be initialized (e.g. due to a bad ``*.pyc`` file).
 
-- Issue #25177: Fixed problem with the mean of very small and very large
+- bpo-25177: Fixed problem with the mean of very small and very large
   numbers. As a side effect, statistics.mean and statistics.variance should
   be significantly faster.
 
-- Issue #25718: Fixed copying object with state with boolean value is false.
+- bpo-25718: Fixed copying object with state with boolean value is false.
 
-- Issue #10131: Fixed deep copying of minidom documents.  Based on patch
-  by Marian Ganisin.
+- bpo-10131: Fixed deep copying of minidom documents.  Based on patch by
+  Marian Ganisin.
 
-- Issue #25725: Fixed a reference leak in pickle.loads() when unpickling
+- bpo-25725: Fixed a reference leak in pickle.loads() when unpickling
   invalid data including tuple instructions.
 
-- Issue #25663: In the Readline completer, avoid listing duplicate global
+- bpo-25663: In the Readline completer, avoid listing duplicate global
   names, and search the global namespace before searching builtins.
 
-- Issue #25688: Fixed file leak in ElementTree.iterparse() raising an error.
+- bpo-25688: Fixed file leak in ElementTree.iterparse() raising an error.
 
-- Issue #23914: Fixed SystemError raised by unpickler on broken pickle data.
+- bpo-23914: Fixed SystemError raised by unpickler on broken pickle data.
 
-- Issue #25691: Fixed crash on deleting ElementTree.Element attributes.
+- bpo-25691: Fixed crash on deleting ElementTree.Element attributes.
 
-- Issue #25624: ZipFile now always writes a ZIP_STORED header for directory
+- bpo-25624: ZipFile now always writes a ZIP_STORED header for directory
   entries.  Patch by Dingyuan Wang.
 
-- Skip getaddrinfo if host is already resolved.
-  Patch by A. Jesse Jiryu Davis.
+- Skip getaddrinfo if host is already resolved. Patch by A. Jesse Jiryu
+  Davis.
 
-- Issue #26050: Add asyncio.StreamReader.readuntil() method.
-  Patch by Марк Коренберг.
+- bpo-26050: Add asyncio.StreamReader.readuntil() method. Patch by Марк
+  Коренберг.
 
-- Issue #25924: Avoid unnecessary serialization of getaddrinfo(3) calls on
-  OS X versions 10.5 or higher.  Original patch by A. Jesse Jiryu Davis.
+- bpo-25924: Avoid unnecessary serialization of getaddrinfo(3) calls on OS X
+  versions 10.5 or higher.  Original patch by A. Jesse Jiryu Davis.
 
-- Issue #26406: Avoid unnecessary serialization of getaddrinfo(3) calls on
+- bpo-26406: Avoid unnecessary serialization of getaddrinfo(3) calls on
   current versions of OpenBSD and NetBSD.  Patch by A. Jesse Jiryu Davis.
 
-- Issue #26848: Fix asyncio/subprocess.communicate() to handle empty input.
+- bpo-26848: Fix asyncio/subprocess.communicate() to handle empty input.
   Patch by Jack O'Connor.
 
-- Issue #27040: Add loop.get_exception_handler method
+- bpo-27040: Add loop.get_exception_handler method
 
-- Issue #27041: asyncio: Add loop.create_future method
+- bpo-27041: asyncio: Add loop.create_future method
 
-- Issue #27223: asyncio: Fix _read_ready and _write_ready to respect
-  _conn_lost.
-  Patch by Łukasz Langa.
+- bpo-27223: asyncio: Fix _read_ready and _write_ready to respect
+  _conn_lost. Patch by Łukasz Langa.
 
-- Issue #22970: asyncio: Fix inconsistency cancelling Condition.wait.
-  Patch by David Coles.
+- bpo-22970: asyncio: Fix inconsistency cancelling Condition.wait. Patch by
+  David Coles.
 
 IDLE
 ----
 
-- Issue #5124: Paste with text selected now replaces the selection on X11.
-  This matches how paste works on Windows, Mac, most modern Linux apps,
-  and ttk widgets.  Original patch by Serhiy Storchaka.
+- bpo-5124: Paste with text selected now replaces the selection on X11. This
+  matches how paste works on Windows, Mac, most modern Linux apps, and ttk
+  widgets. Original patch by Serhiy Storchaka.
 
-- Issue #24759: Make clear in idlelib.idle_test.__init__ that the directory
-  is a private implementation of test.test_idle and tool for maintainers.
+- bpo-24759: Make clear in idlelib.idle_test.__init__ that the directory is
+  a private implementation of test.test_idle and tool for maintainers.
 
-- Issue #27196: Stop 'ThemeChanged' warnings when running IDLE tests.
-  These persisted after other warnings were suppressed in #20567.
-  Apply Serhiy Storchaka's update_idletasks solution to four test files.
-  Record this additional advice in idle_test/README.txt
+- bpo-27196: Stop 'ThemeChanged' warnings when running IDLE tests. These
+  persisted after other warnings were suppressed in #20567. Apply Serhiy
+  Storchaka's update_idletasks solution to four test files. Record this
+  additional advice in idle_test/README.txt
 
-- Issue #20567: Revise idle_test/README.txt with advice about avoiding
-  tk warning messages from tests.  Apply advice to several IDLE tests.
+- bpo-20567: Revise idle_test/README.txt with advice about avoiding tk
+  warning messages from tests.  Apply advice to several IDLE tests.
 
-- Issue #27117: Make colorizer htest and turtledemo work with dark themes.
-  Move code for configuring text widget colors to a new function.
+- bpo-27117: Make colorizer htest and turtledemo work with dark themes. Move
+  code for configuring text widget colors to a new function.
 
-- Issue #26673: When tk reports font size as 0, change to size 10.
-  Such fonts on Linux prevented the configuration dialog from opening.
+- bpo-26673: When tk reports font size as 0, change to size 10. Such fonts
+  on Linux prevented the configuration dialog from opening.
 
-- Issue #21939: Add test for IDLE's percolator.
-  Original patch by Saimadhav Heblikar.
+- bpo-21939: Add test for IDLE's percolator. Original patch by Saimadhav
+  Heblikar.
 
-- Issue #21676: Add test for IDLE's replace dialog.
-  Original patch by Saimadhav Heblikar.
+- bpo-21676: Add test for IDLE's replace dialog. Original patch by Saimadhav
+  Heblikar.
 
-- Issue #18410: Add test for IDLE's search dialog.
-  Original patch by Westley Martínez.
+- bpo-18410: Add test for IDLE's search dialog. Original patch by Westley
+  Martínez.
 
-- Issue #21703: Add test for IDLE's undo delegator.
-  Original patch by Saimadhav Heblikar .
+- bpo-21703: Add test for IDLE's undo delegator. Original patch by Saimadhav
+  Heblikar .
 
-- Issue #27044: Add ConfigDialog.remove_var_callbacks to stop memory leaks.
+- bpo-27044: Add ConfigDialog.remove_var_callbacks to stop memory leaks.
 
-- Issue #23977: Add more asserts to test_delegator.
+- bpo-23977: Add more asserts to test_delegator.
 
-- Issue #20640: Add tests for idlelib.configHelpSourceEdit.
-  Patch by Saimadhav Heblikar.
+- bpo-20640: Add tests for idlelib.configHelpSourceEdit. Patch by Saimadhav
+  Heblikar.
 
-- In the 'IDLE-console differences' section of the IDLE doc, clarify
-  how running with IDLE affects sys.modules and the standard streams.
+- In the 'IDLE-console differences' section of the IDLE doc, clarify how
+  running with IDLE affects sys.modules and the standard streams.
 
-- Issue #25507: fix incorrect change in IOBinding that prevented printing.
+- bpo-25507: fix incorrect change in IOBinding that prevented printing.
   Augment IOBinding htest to include all major IOBinding functions.
 
-- Issue #25905: Revert unwanted conversion of ' to ’ RIGHT SINGLE QUOTATION
-  MARK in README.txt and open this and NEWS.txt with 'ascii'.
-  Re-encode CREDITS.txt to utf-8 and open it with 'utf-8'.
+- bpo-25905: Revert unwanted conversion of ' to ’ RIGHT SINGLE QUOTATION
+  MARK in README.txt and open this and NEWS.txt with 'ascii'. Re-encode
+  CREDITS.txt to utf-8 and open it with 'utf-8'.
 
 Documentation
 -------------
 
-- Issue #19489: Moved the search box from the sidebar to the header and footer
-  of each page.  Patch by Ammar Askar.
+- bpo-19489: Moved the search box from the sidebar to the header and footer
+  of each page. Patch by Ammar Askar.
 
-- Issue #24136: Document the new PEP 448 unpacking syntax of 3.5.
+- bpo-24136: Document the new PEP 448 unpacking syntax of 3.5.
 
-- Issue #26736: Used HTTPS for external links in the documentation if possible.
+- bpo-26736: Used HTTPS for external links in the documentation if possible.
 
-- Issue #6953: Rework the Readline module documentation to group related
+- bpo-6953: Rework the Readline module documentation to group related
   functions together, and add more details such as what underlying Readline
   functions and variables are accessed.
 
-- Issue #23606: Adds note to ctypes documentation regarding cdll.msvcrt.
+- bpo-23606: Adds note to ctypes documentation regarding cdll.msvcrt.
 
-- Issue #25500: Fix documentation to not claim that __import__ is searched for
+- bpo-25500: Fix documentation to not claim that __import__ is searched for
   in the global scope.
 
-- Issue #26014: Update 3.x packaging documentation:
-  * "See also" links to the new docs are now provided in the legacy pages
-  * links to setuptools documentation have been updated
+- bpo-26014: Update 3.x packaging documentation: * "See also" links to the
+  new docs are now provided in the legacy pages * links to setuptools
+  documentation have been updated
 
 Tests
 -----
 
-- Issue #21916: Added tests for the turtle module.  Patch by ingrid,
-  Gregory Loyse and Jelle Zijlstra.
+- bpo-21916: Added tests for the turtle module.  Patch by ingrid, Gregory
+  Loyse and Jelle Zijlstra.
 
-- Issue #26523: The multiprocessing thread pool (multiprocessing.dummy.Pool)
+- bpo-26523: The multiprocessing thread pool (multiprocessing.dummy.Pool)
   was untested.
 
-- Issue #26015: Added new tests for pickling iterators of mutable sequences.
+- bpo-26015: Added new tests for pickling iterators of mutable sequences.
 
-- Issue #26325: Added test.support.check_no_resource_warning() to check that
-  no ResourceWarning is emitted.
+- bpo-26325: Added test.support.check_no_resource_warning() to check that no
+  ResourceWarning is emitted.
 
-- Issue #25940: Changed test_ssl to use self-signed.pythontest.net.  This
+- bpo-25940: Changed test_ssl to use self-signed.pythontest.net.  This
   avoids relying on svn.python.org, which recently changed root certificate.
 
-- Issue #25616: Tests for OrderedDict are extracted from test_collections
-  into separate file test_ordered_dict.
+- bpo-25616: Tests for OrderedDict are extracted from test_collections into
+  separate file test_ordered_dict.
 
-- Issue #26583: Skip test_timestamp_overflow in test_import if bytecode
-  files cannot be written.
+- bpo-26583: Skip test_timestamp_overflow in test_import if bytecode files
+  cannot be written.
 
 Build
 -----
 
-- Issue #26884: Fix linking extension modules for cross builds.
-  Patch by Xavier de Gaye.
+- bpo-26884: Fix linking extension modules for cross builds. Patch by Xavier
+  de Gaye.
 
-- Issue #22359: Disable the rules for running _freeze_importlib and pgen when
-  cross-compiling.  The output of these programs is normally saved with the
+- bpo-22359: Disable the rules for running _freeze_importlib and pgen when
+  cross- compiling.  The output of these programs is normally saved with the
   source code anyway, and is still regenerated when doing a native build.
   Patch by Xavier de Gaye.
 
-- Issue #27229: Fix the cross-compiling pgen rule for in-tree builds.  Patch
-  by Xavier de Gaye.
+- bpo-27229: Fix the cross-compiling pgen rule for in-tree builds.  Patch by
+  Xavier de Gaye.
 
-- Issue #21668: Link audioop, _datetime, _ctypes_test modules to libm,
-  except on Mac OS X. Patch written by Xavier de Gaye.
+- bpo-21668: Link audioop, _datetime, _ctypes_test modules to libm, except
+  on Mac OS X. Patch written by Xavier de Gaye.
 
-- Issue #25702: A --with-lto configure option has been added that will
-  enable link time optimizations at build time during a make profile-opt.
-  Some compilers and toolchains are known to not produce stable code when
-  using LTO, be sure to test things thoroughly before relying on it.
-  It can provide a few % speed up over profile-opt alone.
+- bpo-25702: A --with-lto configure option has been added that will enable
+  link time optimizations at build time during a make profile-opt. Some
+  compilers and toolchains are known to not produce stable code when using
+  LTO, be sure to test things thoroughly before relying on it. It can
+  provide a few % speed up over profile-opt alone.
 
-- Issue #26624: Adds validation of ucrtbase[d].dll version with warning
-  for old versions.
+- bpo-26624: Adds validation of ucrtbase[d].dll version with warning for old
+  versions.
 
-- Issue #17603: Avoid error about nonexistant fileblocks.o file by using a
+- bpo-17603: Avoid error about nonexistant fileblocks.o file by using a
   lower-level check for st_blocks in struct stat.
 
-- Issue #26079: Fixing the build output folder for tix-8.4.3.6. Patch by
-  Bjoern Thiel.
+- bpo-26079: Fixing the build output folder for tix-8.4.3.6. Patch by Bjoern
+  Thiel.
 
-- Issue #26465: Update Windows builds to use OpenSSL 1.0.2g.
+- bpo-26465: Update Windows builds to use OpenSSL 1.0.2g.
 
-- Issue #24421: Compile Modules/_math.c once, before building extensions.
+- bpo-24421: Compile Modules/_math.c once, before building extensions.
   Previously it could fail to compile properly if the math and cmath builds
   were concurrent.
 
-- Issue #25348: Added ``--pgo`` and ``--pgo-job`` arguments to
+- bpo-25348: Added ``--pgo`` and ``--pgo-job`` arguments to
   ``PCbuild\build.bat`` for building with Profile-Guided Optimization.  The
   old ``PCbuild\build_pgo.bat`` script is now deprecated, and simply calls
   ``PCbuild\build.bat --pgo %*``.
 
-- Issue #25827: Add support for building with ICC to ``configure``, including
-  new ``--with-icc`` flag.
+- bpo-25827: Add support for building with ICC to ``configure``, including a
+  new ``--with-icc`` flag.
 
-- Issue #25696: Fix installation of Python on UNIX with make -j9.
+- bpo-25696: Fix installation of Python on UNIX with make -j9.
 
-- Issue #26930: Update OS X 10.5+ 32-bit-only installer to build
-  and link with OpenSSL 1.0.2h.
+- bpo-26930: Update OS X 10.5+ 32-bit-only installer to build and link with
+  OpenSSL 1.0.2h.
 
-- Issue #26268: Update Windows builds to use OpenSSL 1.0.2f.
+- bpo-26268: Update Windows builds to use OpenSSL 1.0.2f.
 
-- Issue #25136: Support Apple Xcode 7's new textual SDK stub libraries.
+- bpo-25136: Support Apple Xcode 7's new textual SDK stub libraries.
 
-- Issue #24324: Do not enable unreachable code warnings when using
-  gcc as the option does not work correctly in older versions of gcc
-  and has been silently removed as of gcc-4.5.
+- bpo-24324: Do not enable unreachable code warnings when using gcc as the
+  option does not work correctly in older versions of gcc and has been
+  silently removed as of gcc-4.5.
 
 Windows
 -------
 
-- Issue #27053: Updates make_zip.py to correctly generate library ZIP file.
+- bpo-27053: Updates make_zip.py to correctly generate library ZIP file.
 
-- Issue #26268: Update the prepare_ssl.py script to handle OpenSSL releases
+- bpo-26268: Update the prepare_ssl.py script to handle OpenSSL releases
   that don't include the contents of the include directory (that is, 1.0.2e
   and later).
 
-- Issue #26071: bdist_wininst created binaries fail to start and find
-  32bit Python
+- bpo-26071: bdist_wininst created binaries fail to start and find 32bit
+  Python
 
-- Issue #26073: Update the list of magic numbers in launcher
+- bpo-26073: Update the list of magic numbers in launcher
 
-- Issue #26065: Excludes venv from library when generating embeddable
-  distro.
+- bpo-26065: Excludes venv from library when generating embeddable distro.
 
 Tools/Demos
 -----------
 
-- Issue #26799: Fix python-gdb.py: don't get C types once when the Python code
-  is loaded, but get C types on demand. The C types can change if
-  python-gdb.py is loaded before the Python executable. Patch written by Thomas
+- bpo-26799: Fix python-gdb.py: don't get C types once when the Python code
+  is loaded, but get C types on demand. The C types can change if python-
+  gdb.py is loaded before the Python executable. Patch written by Thomas
   Ilsche.
 
-- Issue #26271: Fix the Freeze tool to properly use flags passed through
+- bpo-26271: Fix the Freeze tool to properly use flags passed through
   configure. Patch by Daniel Shaulov.
 
-- Issue #26489: Add dictionary unpacking support to Tools/parser/unparse.py.
+- bpo-26489: Add dictionary unpacking support to Tools/parser/unparse.py.
   Patch by Guo Ci Teo.
 
-- Issue #26316: Fix variable name typo in Argument Clinic.
+- bpo-26316: Fix variable name typo in Argument Clinic.
 
-Misc
-----
+Windows
+-------
 
-- Issue #17500, and https://github.com/python/pythondotorg/issues/945: Remove
-  unused and outdated icons.
+- bpo-17500: Remove unused and outdated icons. (See also:
+  https://github.com/python/pythondotorg/issues/945)
 
 
 What's New in Python 3.5.1 final?
 =================================
 
-Release date: 2015-12-06
+*Release date: 2015-12-06*
 
 Core and Builtins
 -----------------
 
-- Issue #25709: Fixed problem with in-place string concatenation and
-  utf-8 cache.
+- bpo-25709: Fixed problem with in-place string concatenation and utf-8
+  cache.
 
 Windows
 -------
 
-- Issue #25715: Python 3.5.1 installer shows wrong upgrade path and incorrect
+- bpo-25715: Python 3.5.1 installer shows wrong upgrade path and incorrect
   logic for launcher detection.
 
 
 What's New in Python 3.5.1 release candidate 1?
 ===============================================
 
-Release date: 2015-11-22
+*Release date: 2015-11-22*
 
 Core and Builtins
 -----------------
 
-- Issue #25630: Fix a possible segfault during argument parsing in functions
+- bpo-25630: Fix a possible segfault during argument parsing in functions
   that accept filesystem paths.
 
-- Issue #23564: Fixed a partially broken sanity check in the _posixsubprocess
+- bpo-23564: Fixed a partially broken sanity check in the _posixsubprocess
   internals regarding how fds_to_pass were passed to the child.  The bug had
   no actual impact as subprocess.py already avoided it.
 
-- Issue #25388: Fixed tokenizer crash when processing undecodable source code
+- bpo-25388: Fixed tokenizer crash when processing undecodable source code
   with a null byte.
 
-- Issue #25462: The hash of the key now is calculated only once in most
+- bpo-25462: The hash of the key now is calculated only once in most
   operations in C implementation of OrderedDict.
 
-- Issue #22995: Default implementation of __reduce__ and __reduce_ex__ now
+- bpo-22995: Default implementation of __reduce__ and __reduce_ex__ now
   rejects builtin types with not defined __new__.
 
-- Issue #25555: Fix parser and AST: fill lineno and col_offset of "arg" node
+- bpo-25555: Fix parser and AST: fill lineno and col_offset of "arg" node
   when compiling AST from Python objects.
 
-- Issue #24802: Avoid buffer overreads when int(), float(), compile(), exec()
+- bpo-24802: Avoid buffer overreads when int(), float(), compile(), exec()
   and eval() are passed bytes-like objects.  These objects are not
-  necessarily terminated by a null byte, but the functions assumed they were.
+  necessarily terminated by a null byte, but the functions assumed they
+  were.
 
-- Issue #24726: Fixed a crash and leaking NULL in repr() of OrderedDict that
+- bpo-24726: Fixed a crash and leaking NULL in repr() of OrderedDict that
   was mutated by direct calls of dict methods.
 
-- Issue #25449: Iterating OrderedDict with keys with unstable hash now raises
+- bpo-25449: Iterating OrderedDict with keys with unstable hash now raises
   KeyError in C implementations as well as in Python implementation.
 
-- Issue #25395: Fixed crash when highly nested OrderedDict structures were
+- bpo-25395: Fixed crash when highly nested OrderedDict structures were
   garbage collected.
 
-- Issue #25274: sys.setrecursionlimit() now raises a RecursionError if the new
-  recursion limit is too low depending at the current recursion depth. Modify
-  also the "lower-water mark" formula to make it monotonic. This mark is used
-  to decide when the overflowed flag of the thread state is reset.
+- bpo-25274: sys.setrecursionlimit() now raises a RecursionError if the new
+  recursion limit is too low depending at the current recursion depth.
+  Modify also the "lower-water mark" formula to make it monotonic. This mark
+  is used to decide when the overflowed flag of the thread state is reset.
 
-- Issue #24402: Fix input() to prompt to the redirected stdout when
+- bpo-24402: Fix input() to prompt to the redirected stdout when
   sys.stdout.fileno() fails.
 
-- Issue #24806: Prevent builtin types that are not allowed to be subclassed from
-  being subclassed through multiple inheritance.
+- bpo-24806: Prevent builtin types that are not allowed to be subclassed
+  from being subclassed through multiple inheritance.
 
-- Issue #24848: Fixed a number of bugs in UTF-7 decoding of misformed data.
+- bpo-24848: Fixed a number of bugs in UTF-7 decoding of misformed data.
 
-- Issue #25280: Import trace messages emitted in verbose (-v) mode are no
+- bpo-25280: Import trace messages emitted in verbose (-v) mode are no
   longer formatted twice.
 
-- Issue #25003: On Solaris 11.3 or newer, os.urandom() now uses the
-  getrandom() function instead of the getentropy() function. The getentropy()
-  function is blocking to generate very good quality entropy, os.urandom()
-  doesn't need such high-quality entropy.
+- bpo-25003: On Solaris 11.3 or newer, os.urandom() now uses the getrandom()
+  function instead of the getentropy() function. The getentropy() function
+  is blocking to generate very good quality entropy, os.urandom() doesn't
+  need such high- quality entropy.
 
-- Issue #25182: The stdprinter (used as sys.stderr before the io module is
+- bpo-25182: The stdprinter (used as sys.stderr before the io module is
   imported at startup) now uses the backslashreplace error handler.
 
-- Issue #25131: Make the line number and column offset of set/dict literals and
+- bpo-25131: Make the line number and column offset of set/dict literals and
   comprehensions correspond to the opening brace.
 
-- Issue #25150: Hide the private _Py_atomic_xxx symbols from the public
-  Python.h header to fix a compilation error with OpenMP. PyThreadState_GET()
-  becomes an alias to PyThreadState_Get() to avoid ABI incompatibilies.
+- bpo-25150: Hide the private _Py_atomic_xxx symbols from the public
+  Python.h header to fix a compilation error with OpenMP.
+  PyThreadState_GET() becomes an alias to PyThreadState_Get() to avoid ABI
+  incompatibilies.
 
 Library
 -------
 
-- Issue #25626: Change three zlib functions to accept sizes that fit in
+- bpo-25626: Change three zlib functions to accept sizes that fit in
   Py_ssize_t, but internally cap those sizes to UINT_MAX.  This resolves a
-  regression in 3.5 where GzipFile.read() failed to read chunks larger than 2
-  or 4 GiB.  The change affects the zlib.Decompress.decompress() max_length
-  parameter, the zlib.decompress() bufsize parameter, and the
+  regression in 3.5 where GzipFile.read() failed to read chunks larger than
+  2 or 4 GiB.  The change affects the zlib.Decompress.decompress()
+  max_length parameter, the zlib.decompress() bufsize parameter, and the
   zlib.Decompress.flush() length parameter.
 
-- Issue #25583: Avoid incorrect errors raised by os.makedirs(exist_ok=True)
+- bpo-25583: Avoid incorrect errors raised by os.makedirs(exist_ok=True)
   when the OS gives priority to errors such as EACCES over EEXIST.
 
-- Issue #25593: Change semantics of EventLoop.stop() in asyncio.
+- bpo-25593: Change semantics of EventLoop.stop() in asyncio.
 
-- Issue #6973: When we know a subprocess.Popen process has died, do
-  not allow the send_signal(), terminate(), or kill() methods to do
-  anything as they could potentially signal a different process.
+- bpo-6973: When we know a subprocess.Popen process has died, do not allow
+  the send_signal(), terminate(), or kill() methods to do anything as they
+  could potentially signal a different process.
 
-- Issue #25590: In the Readline completer, only call getattr() once per
+- bpo-25590: In the Readline completer, only call getattr() once per
   attribute.
 
-- Issue #25498: Fix a crash when garbage-collecting ctypes objects created
-  by wrapping a memoryview.  This was a regression made in 3.5a1.  Based
-  on patch by Eryksun.
+- bpo-25498: Fix a crash when garbage-collecting ctypes objects created by
+  wrapping a memoryview.  This was a regression made in 3.5a1.  Based on
+  patch by Eryksun.
 
-- Issue #25584: Added "escape" to the __all__ list in the glob module.
+- bpo-25584: Added "escape" to the __all__ list in the glob module.
 
-- Issue #25584: Fixed recursive glob() with patterns starting with ``**``.
+- bpo-25584: Fixed recursive glob() with patterns starting with ``**``.
 
-- Issue #25446: Fix regression in smtplib's AUTH LOGIN support.
+- bpo-25446: Fix regression in smtplib's AUTH LOGIN support.
 
-- Issue #18010: Fix the pydoc web server's module search function to handle
+- bpo-18010: Fix the pydoc web server's module search function to handle
   exceptions from importing packages.
 
-- Issue #25554: Got rid of circular references in regular expression parsing.
+- bpo-25554: Got rid of circular references in regular expression parsing.
 
-- Issue #25510: fileinput.FileInput.readline() now returns b'' instead of ''
-  at the end if the FileInput was opened with binary mode.
-  Patch by Ryosuke Ito.
+- bpo-25510: fileinput.FileInput.readline() now returns b'' instead of '' at
+  the end if the FileInput was opened with binary mode. Patch by Ryosuke
+  Ito.
 
-- Issue #25503: Fixed inspect.getdoc() for inherited docstrings of properties.
+- bpo-25503: Fixed inspect.getdoc() for inherited docstrings of properties.
   Original patch by John Mark Vandenberg.
 
-- Issue #25515: Always use os.urandom as a source of randomness in uuid.uuid4.
+- bpo-25515: Always use os.urandom as a source of randomness in uuid.uuid4.
 
-- Issue #21827: Fixed textwrap.dedent() for the case when largest common
-  whitespace is a substring of smallest leading whitespace.
-  Based on patch by Robert Li.
+- bpo-21827: Fixed textwrap.dedent() for the case when largest common
+  whitespace is a substring of smallest leading whitespace. Based on patch
+  by Robert Li.
 
-- Issue #25447: The lru_cache() wrapper objects now can be copied and pickled
+- bpo-25447: The lru_cache() wrapper objects now can be copied and pickled
   (by returning the original object unchanged).
 
-- Issue #25390: typing: Don't crash on Union[str, Pattern].
+- bpo-25390: typing: Don't crash on Union[str, Pattern].
 
-- Issue #25441: asyncio: Raise error from drain() when socket is closed.
+- bpo-25441: asyncio: Raise error from drain() when socket is closed.
 
-- Issue #25410: Cleaned up and fixed minor bugs in C implementation of
+- bpo-25410: Cleaned up and fixed minor bugs in C implementation of
   OrderedDict.
 
-- Issue #25411: Improved Unicode support in SMTPHandler through better use of
+- bpo-25411: Improved Unicode support in SMTPHandler through better use of
   the email package. Thanks to user simon04 for the patch.
 
-- Issue #25407: Remove mentions of the formatter module being removed in
-  Python 3.6.
+- bpo-25407: Remove mentions of the formatter module being removed in Python
+  3.6.
 
-- Issue #25406: Fixed a bug in C implementation of OrderedDict.move_to_end()
+- bpo-25406: Fixed a bug in C implementation of OrderedDict.move_to_end()
   that caused segmentation fault or hang in iterating after moving several
   items to the start of ordered dict.
 
-- Issue #25364: zipfile now works in threads disabled builds.
+- bpo-25364: zipfile now works in threads disabled builds.
 
-- Issue #25328: smtpd's SMTPChannel now correctly raises a ValueError if both
+- bpo-25328: smtpd's SMTPChannel now correctly raises a ValueError if both
   decode_data and enable_SMTPUTF8 are set to true.
 
-- Issue #25316: distutils raises OSError instead of DistutilsPlatformError
-  when MSVC is not installed.
+- bpo-25316: distutils raises OSError instead of DistutilsPlatformError when
+  MSVC is not installed.
 
-- Issue #25380: Fixed protocol for the STACK_GLOBAL opcode in
+- bpo-25380: Fixed protocol for the STACK_GLOBAL opcode in
   pickletools.opcodes.
 
-- Issue #23972: Updates asyncio datagram create method allowing reuseport
-  and reuseaddr socket options to be set prior to binding the socket.
-  Mirroring the existing asyncio create_server method the reuseaddr option
-  for datagram sockets defaults to True if the O/S is 'posix' (except if the
+- bpo-23972: Updates asyncio datagram create method allowing reuseport and
+  reuseaddr socket options to be set prior to binding the socket. Mirroring
+  the existing asyncio create_server method the reuseaddr option for
+  datagram sockets defaults to True if the O/S is 'posix' (except if the
   platform is Cygwin). Patch by Chris Laws.
 
-- Issue #25304: Add asyncio.run_coroutine_threadsafe().  This lets you
-  submit a coroutine to a loop from another thread, returning a
+- bpo-25304: Add asyncio.run_coroutine_threadsafe().  This lets you submit a
+  coroutine to a loop from another thread, returning a
   concurrent.futures.Future.  By Vincent Michel.
 
-- Issue #25232: Fix CGIRequestHandler to split the query from the URL at the
+- bpo-25232: Fix CGIRequestHandler to split the query from the URL at the
   first question mark (?) rather than the last. Patch from Xiang Zhang.
 
-- Issue #24657: Prevent CGIRequestHandler from collapsing slashes in the
-  query part of the URL as if it were a path. Patch from Xiang Zhang.
+- bpo-24657: Prevent CGIRequestHandler from collapsing slashes in the query
+  part of the URL as if it were a path. Patch from Xiang Zhang.
 
-- Issue #24483: C implementation of functools.lru_cache() now calculates key's
+- bpo-24483: C implementation of functools.lru_cache() now calculates key's
   hash only once.
 
-- Issue #22958: Constructor and update method of weakref.WeakValueDictionary
+- bpo-22958: Constructor and update method of weakref.WeakValueDictionary
   now accept the self and the dict keyword arguments.
 
-- Issue #22609: Constructor of collections.UserDict now accepts the self keyword
-  argument.
+- bpo-22609: Constructor of collections.UserDict now accepts the self
+  keyword argument.
 
-- Issue #25111: Fixed comparison of traceback.FrameSummary.
+- bpo-25111: Fixed comparison of traceback.FrameSummary.
 
-- Issue #25262: Added support for BINBYTES8 opcode in Python implementation of
-  unpickler.  Highest 32 bits of 64-bit size for BINUNICODE8 and BINBYTES8
-  opcodes no longer silently ignored on 32-bit platforms in C implementation.
+- bpo-25262: Added support for BINBYTES8 opcode in Python implementation of
+  unpickler. Highest 32 bits of 64-bit size for BINUNICODE8 and BINBYTES8
+  opcodes no longer silently ignored on 32-bit platforms in C
+  implementation.
 
-- Issue #25034: Fix string.Formatter problem with auto-numbering and
-  nested format_specs. Patch by Anthon van der Neut.
+- bpo-25034: Fix string.Formatter problem with auto-numbering and nested
+  format_specs. Patch by Anthon van der Neut.
 
-- Issue #25233: Rewrite the guts of asyncio.Queue and
-  asyncio.Semaphore to be more understandable and correct.
+- bpo-25233: Rewrite the guts of asyncio.Queue and asyncio.Semaphore to be
+  more understandable and correct.
 
-- Issue #25203: Failed readline.set_completer_delims() no longer left the
+- bpo-25203: Failed readline.set_completer_delims() no longer left the
   module in inconsistent state.
 
-- Issue #23600: Default implementation of tzinfo.fromutc() was returning
-  wrong results in some cases.
+- bpo-23600: Default implementation of tzinfo.fromutc() was returning wrong
+  results in some cases.
 
-- Issue #23329: Allow the ssl module to be built with older versions of
+- bpo-23329: Allow the ssl module to be built with older versions of
   LibreSSL.
 
 - Prevent overflow in _Unpickler_Read.
 
-- Issue #25047: The XML encoding declaration written by Element Tree now
+- bpo-25047: The XML encoding declaration written by Element Tree now
   respects the letter case given by the user. This restores the ability to
   write encoding names in uppercase like "UTF-8", which worked in Python 2.
 
-- Issue #25135: Make deque_clear() safer by emptying the deque before clearing.
+- bpo-25135: Make deque_clear() safer by emptying the deque before clearing.
   This helps avoid possible reentrancy issues.
 
-- Issue #19143: platform module now reads Windows version from kernel32.dll to
+- bpo-19143: platform module now reads Windows version from kernel32.dll to
   avoid compatibility shims.
 
-- Issue #25092: Fix datetime.strftime() failure when errno was already set to
+- bpo-25092: Fix datetime.strftime() failure when errno was already set to
   EINVAL.
 
-- Issue #23517: Fix rounding in fromtimestamp() and utcfromtimestamp() methods
+- bpo-23517: Fix rounding in fromtimestamp() and utcfromtimestamp() methods
   of datetime.datetime: microseconds are now rounded to nearest with ties
   going to nearest even integer (ROUND_HALF_EVEN), instead of being rounding
-  towards minus infinity (ROUND_FLOOR). It's important that these methods use
-  the same rounding mode than datetime.timedelta to keep the property:
-  (datetime(1970,1,1) + timedelta(seconds=t)) == datetime.utcfromtimestamp(t).
-  It also the rounding mode used by round(float) for example.
+  towards minus infinity (ROUND_FLOOR). It's important that these methods
+  use the same rounding mode than datetime.timedelta to keep the property:
+  (datetime(1970,1,1) + timedelta(seconds=t)) ==
+  datetime.utcfromtimestamp(t). It also the rounding mode used by
+  round(float) for example.
 
-- Issue #25155: Fix datetime.datetime.now() and datetime.datetime.utcnow() on
+- bpo-25155: Fix datetime.datetime.now() and datetime.datetime.utcnow() on
   Windows to support date after year 2038. It was a regression introduced in
   Python 3.5.0.
 
-- Issue #25108: Omitted internal frames in traceback functions print_stack(),
+- bpo-25108: Omitted internal frames in traceback functions print_stack(),
   format_stack(), and extract_stack() called without arguments.
 
-- Issue #25118: Fix a regression of Python 3.5.0 in os.waitpid() on Windows.
+- bpo-25118: Fix a regression of Python 3.5.0 in os.waitpid() on Windows.
 
-- Issue #24684: socket.socket.getaddrinfo() now calls
+- bpo-24684: socket.socket.getaddrinfo() now calls
   PyUnicode_AsEncodedString() instead of calling the encode() method of the
-  host, to handle correctly custom string with an encode() method which doesn't
-  return a byte string. The encoder of the IDNA codec is now called directly
-  instead of calling the encode() method of the string.
+  host, to handle correctly custom string with an encode() method which
+  doesn't return a byte string. The encoder of the IDNA codec is now called
+  directly instead of calling the encode() method of the string.
 
-- Issue #25060: Correctly compute stack usage of the BUILD_MAP opcode.
+- bpo-25060: Correctly compute stack usage of the BUILD_MAP opcode.
 
-- Issue #24857: Comparing call_args to a long sequence now correctly returns a
+- bpo-24857: Comparing call_args to a long sequence now correctly returns a
   boolean result instead of raising an exception.  Patch by A Kaptur.
 
-- Issue #23144: Make sure that HTMLParser.feed() returns all the data, even
+- bpo-23144: Make sure that HTMLParser.feed() returns all the data, even
   when convert_charrefs is True.
 
-- Issue #24982: shutil.make_archive() with the "zip" format now adds entries
+- bpo-24982: shutil.make_archive() with the "zip" format now adds entries
   for directories (including empty directories) in ZIP file.
 
-- Issue #25019: Fixed a crash caused by setting non-string key of expat parser.
+- bpo-25019: Fixed a crash caused by setting non-string key of expat parser.
   Based on patch by John Leitch.
 
-- Issue #16180: Exit pdb if file has syntax error, instead of trapping user
-  in an infinite loop.  Patch by Xavier de Gaye.
+- bpo-16180: Exit pdb if file has syntax error, instead of trapping user in
+  an infinite loop.  Patch by Xavier de Gaye.
 
-- Issue #24891: Fix a race condition at Python startup if the file descriptor
+- bpo-24891: Fix a race condition at Python startup if the file descriptor
   of stdin (0), stdout (1) or stderr (2) is closed while Python is creating
   sys.stdin, sys.stdout and sys.stderr objects. These attributes are now set
-  to None if the creation of the object failed, instead of raising an OSError
-  exception. Initial patch written by Marco Paolini.
+  to None if the creation of the object failed, instead of raising an
+  OSError exception. Initial patch written by Marco Paolini.
 
-- Issue #24992: Fix error handling and a race condition (related to garbage
+- bpo-24992: Fix error handling and a race condition (related to garbage
   collection) in collections.OrderedDict constructor.
 
-- Issue #24881: Fixed setting binary mode in Python implementation of FileIO
-  on Windows and Cygwin.  Patch from Akira Li.
+- bpo-24881: Fixed setting binary mode in Python implementation of FileIO on
+  Windows and Cygwin.  Patch from Akira Li.
 
-- Issue #25578: Fix (another) memory leak in SSLSocket.getpeercer().
+- bpo-25578: Fix (another) memory leak in SSLSocket.getpeercer().
 
-- Issue #25530: Disable the vulnerable SSLv3 protocol by default when creating
+- bpo-25530: Disable the vulnerable SSLv3 protocol by default when creating
   ssl.SSLContext.
 
-- Issue #25569: Fix memory leak in SSLSocket.getpeercert().
+- bpo-25569: Fix memory leak in SSLSocket.getpeercert().
 
-- Issue #25471: Sockets returned from accept() shouldn't appear to be
+- bpo-25471: Sockets returned from accept() shouldn't appear to be
   nonblocking.
 
-- Issue #25319: When threading.Event is reinitialized, the underlying condition
+- bpo-25319: When threading.Event is reinitialized, the underlying condition
   should use a regular lock rather than a recursive lock.
 
-- Issue #21112: Fix regression in unittest.expectedFailure on subclasses.
-  Patch from Berker Peksag.
+- bpo-21112: Fix regression in unittest.expectedFailure on subclasses. Patch
+  from Berker Peksag.
 
-- Issue #24764: cgi.FieldStorage.read_multi() now ignores the Content-Length
-  header in part headers. Patch written by Peter Landry and reviewed by Pierre
-  Quentel.
+- bpo-24764: cgi.FieldStorage.read_multi() now ignores the Content-Length
+  header in part headers. Patch written by Peter Landry and reviewed by
+  Pierre Quentel.
 
-- Issue #24913: Fix overrun error in deque.index().
-  Found by John Leitch and Bryce Darling.
+- bpo-24913: Fix overrun error in deque.index(). Found by John Leitch and
+  Bryce Darling.
 
-- Issue #24774: Fix docstring in http.server.test. Patch from Chiu-Hsiang Hsu.
+- bpo-24774: Fix docstring in http.server.test. Patch from Chiu-Hsiang Hsu.
 
-- Issue #21159: Improve message in configparser.InterpolationMissingOptionError.
-  Patch from Łukasz Langa.
+- bpo-21159: Improve message in
+  configparser.InterpolationMissingOptionError. Patch from Łukasz Langa.
 
-- Issue #20362: Honour TestCase.longMessage correctly in assertRegex.
-  Patch from Ilia Kurenkov.
+- bpo-20362: Honour TestCase.longMessage correctly in assertRegex. Patch
+  from Ilia Kurenkov.
 
-- Issue #23572: Fixed functools.singledispatch on classes with falsy
+- bpo-23572: Fixed functools.singledispatch on classes with falsy
   metaclasses.  Patch by Ethan Furman.
 
 - asyncio: ensure_future() now accepts awaitable objects.
@@ -5639,3259 +6213,3273 @@ Library
 IDLE
 ----
 
-- Issue #15348: Stop the debugger engine (normally in a user process)
-  before closing the debugger window (running in the IDLE process).
-  This prevents the RuntimeErrors that were being caught and ignored.
+- bpo-15348: Stop the debugger engine (normally in a user process) before
+  closing the debugger window (running in the IDLE process). This prevents
+  the RuntimeErrors that were being caught and ignored.
 
-- Issue #24455: Prevent IDLE from hanging when a) closing the shell while the
+- bpo-24455: Prevent IDLE from hanging when a) closing the shell while the
   debugger is active (15347); b) closing the debugger with the [X] button
-  (15348); and c) activating the debugger when already active (24455).
-  The patch by Mark Roseman does this by making two changes.
-  1. Suspend and resume the gui.interaction method with the tcl vwait
-  mechanism intended for this purpose (instead of root.mainloop & .quit).
-  2. In gui.run, allow any existing interaction to terminate first.
+  (15348); and c) activating the debugger when already active (24455). The
+  patch by Mark Roseman does this by making two changes. 1. Suspend and
+  resume the gui.interaction method with the tcl vwait mechanism intended
+  for this purpose (instead of root.mainloop & .quit). 2. In gui.run, allow
+  any existing interaction to terminate first.
 
 - Change 'The program' to 'Your program' in an IDLE 'kill program?' message
   to make it clearer that the program referred to is the currently running
   user program, not IDLE itself.
 
-- Issue #24750: Improve the appearance of the IDLE editor window status bar.
+- bpo-24750: Improve the appearance of the IDLE editor window status bar.
   Patch by Mark Roseman.
 
-- Issue #25313: Change the handling of new built-in text color themes to better
+- bpo-25313: Change the handling of new built-in text color themes to better
   address the compatibility problem introduced by the addition of IDLE Dark.
   Consistently use the revised idleConf.CurrentTheme everywhere in idlelib.
 
-- Issue #24782: Extension configuration is now a tab in the IDLE Preferences
+- bpo-24782: Extension configuration is now a tab in the IDLE Preferences
   dialog rather than a separate dialog.  The former tabs are now a sorted
   list.  Patch by Mark Roseman.
 
-- Issue #22726: Re-activate the config dialog help button with some content
+- bpo-22726: Re-activate the config dialog help button with some content
   about the other buttons and the new IDLE Dark theme.
 
-- Issue #24820: IDLE now has an 'IDLE Dark' built-in text color theme.
-  It is more or less IDLE Classic inverted, with a cobalt blue background.
-  Strings, comments, keywords, ... are still green, red, orange, ... .
-  To use it with IDLEs released before November 2015, hit the
-  'Save as New Custom Theme' button and enter a new name,
-  such as 'Custom Dark'.  The custom theme will work with any IDLE
-  release, and can be modified.
+- bpo-24820: IDLE now has an 'IDLE Dark' built-in text color theme. It is
+  more or less IDLE Classic inverted, with a cobalt blue background.
+  Strings, comments, keywords, ... are still green, red, orange, ... . To
+  use it with IDLEs released before November 2015, hit the 'Save as New
+  Custom Theme' button and enter a new name, such as 'Custom Dark'.  The
+  custom theme will work with any IDLE release, and can be modified.
 
-- Issue #25224: README.txt is now an idlelib index for IDLE developers and
-  curious users.  The previous user content is now in the IDLE doc chapter.
+- bpo-25224: README.txt is now an idlelib index for IDLE developers and
+  curious users. The previous user content is now in the IDLE doc chapter.
   'IDLE' now means 'Integrated Development and Learning Environment'.
 
-- Issue #24820: Users can now set breakpoint colors in
-  Settings -> Custom Highlighting.  Original patch by Mark Roseman.
+- bpo-24820: Users can now set breakpoint colors in Settings -> Custom
+  Highlighting. Original patch by Mark Roseman.
 
-- Issue #24972: Inactive selection background now matches active selection
+- bpo-24972: Inactive selection background now matches active selection
   background, as configured by users, on all systems.  Found items are now
   always highlighted on Windows.  Initial patch by Mark Roseman.
 
-- Issue #24570: Idle: make calltip and completion boxes appear on Macs
-  affected by a tk regression.  Initial patch by Mark Roseman.
+- bpo-24570: Idle: make calltip and completion boxes appear on Macs affected
+  by a tk regression.  Initial patch by Mark Roseman.
 
-- Issue #24988: Idle ScrolledList context menus (used in debugger)
-  now work on Mac Aqua.  Patch by Mark Roseman.
+- bpo-24988: Idle ScrolledList context menus (used in debugger) now work on
+  Mac Aqua. Patch by Mark Roseman.
 
-- Issue #24801: Make right-click for context menu work on Mac Aqua.
-  Patch by Mark Roseman.
+- bpo-24801: Make right-click for context menu work on Mac Aqua. Patch by
+  Mark Roseman.
 
-- Issue #25173: Associate tkinter messageboxes with a specific widget.
-  For Mac OSX, make them a 'sheet'.  Patch by Mark Roseman.
+- bpo-25173: Associate tkinter messageboxes with a specific widget. For Mac
+  OSX, make them a 'sheet'.  Patch by Mark Roseman.
 
-- Issue #25198: Enhance the initial html viewer now used for Idle Help.
-  * Properly indent fixed-pitch text (patch by Mark Roseman).
-  * Give code snippet a very Sphinx-like light blueish-gray background.
-  * Re-use initial width and height set by users for shell and editor.
-  * When the Table of Contents (TOC) menu is used, put the section header
-  at the top of the screen.
+- bpo-25198: Enhance the initial html viewer now used for Idle Help. *
+  Properly indent fixed-pitch text (patch by Mark Roseman). * Give code
+  snippet a very Sphinx- like light blueish-gray background. * Re-use
+  initial width and height set by users for shell and editor. * When the
+  Table of Contents (TOC) menu is used, put the section header at the top of
+  the screen.
 
-- Issue #25225: Condense and rewrite Idle doc section on text colors.
+- bpo-25225: Condense and rewrite Idle doc section on text colors.
 
-- Issue #21995: Explain some differences between IDLE and console Python.
+- bpo-21995: Explain some differences between IDLE and console Python.
 
-- Issue #22820: Explain need for *print* when running file from Idle editor.
+- bpo-22820: Explain need for *print* when running file from Idle editor.
 
-- Issue #25224: Doc: augment Idle feature list and no-subprocess section.
+- bpo-25224: Doc: augment Idle feature list and no-subprocess section.
 
-- Issue #25219: Update doc for Idle command line options.
-  Some were missing and notes were not correct.
+- bpo-25219: Update doc for Idle command line options. Some were missing and
+  notes were not correct.
 
-- Issue #24861: Most of idlelib is private and subject to change.
-  Use idleib.idle.* to start Idle. See idlelib.__init__.__doc__.
+- bpo-24861: Most of idlelib is private and subject to change. Use
+  idleib.idle.* to start Idle. See idlelib.__init__.__doc__.
 
-- Issue #25199: Idle: add synchronization comments for future maintainers.
+- bpo-25199: Idle: add synchronization comments for future maintainers.
 
-- Issue #16893: Replace help.txt with help.html for Idle doc display.
-  The new idlelib/help.html is rstripped Doc/build/html/library/idle.html.
-  It looks better than help.txt and will better document Idle as released.
-  The tkinter html viewer that works for this file was written by Mark Roseman.
-  The now unused EditorWindow.HelpDialog class and helt.txt file are deprecated.
+- bpo-16893: Replace help.txt with help.html for Idle doc display. The new
+  idlelib/help.html is rstripped Doc/build/html/library/idle.html. It looks
+  better than help.txt and will better document Idle as released. The
+  tkinter html viewer that works for this file was written by Mark Roseman.
+  The now unused EditorWindow.HelpDialog class and helt.txt file are
+  deprecated.
 
-- Issue #24199: Deprecate unused idlelib.idlever with possible removal in 3.6.
+- bpo-24199: Deprecate unused idlelib.idlever with possible removal in 3.6.
 
-- Issue #24790: Remove extraneous code (which also create 2 & 3 conflicts).
+- bpo-24790: Remove extraneous code (which also create 2 & 3 conflicts).
 
 Documentation
 -------------
 
-- Issue #22558: Add remaining doc links to source code for Python-coded modules.
-  Patch by Yoni Lavi.
+- bpo-22558: Add remaining doc links to source code for Python-coded
+  modules. Patch by Yoni Lavi.
 
-- Issue #12067: Rewrite Comparisons section in the Expressions chapter of the
+- bpo-12067: Rewrite Comparisons section in the Expressions chapter of the
   language reference. Some of the details of comparing mixed types were
   incorrect or ambiguous. NotImplemented is only relevant at a lower level
   than the Expressions chapter. Added details of comparing range() objects,
-  and default behaviour and consistency suggestions for user-defined classes.
-  Patch from Andy Maier.
+  and default behaviour and consistency suggestions for user-defined
+  classes. Patch from Andy Maier.
 
-- Issue #24952: Clarify the default size argument of stack_size() in
-  the "threading" and "_thread" modules. Patch from Mattip.
+- bpo-24952: Clarify the default size argument of stack_size() in the
+  "threading" and "_thread" modules. Patch from Mattip.
 
-- Issue #23725: Overhaul tempfile docs. Note deprecated status of mktemp.
-  Patch from Zbigniew Jędrzejewski-Szmek.
+- bpo-23725: Overhaul tempfile docs. Note deprecated status of mktemp. Patch
+  from Zbigniew Jędrzejewski-Szmek.
 
-- Issue #24808: Update the types of some PyTypeObject fields.  Patch by
-  Joseph Weston.
+- bpo-24808: Update the types of some PyTypeObject fields.  Patch by Joseph
+  Weston.
 
-- Issue #22812: Fix unittest discovery examples.
-  Patch from Pam McA'Nulty.
+- bpo-22812: Fix unittest discovery examples. Patch from Pam McA'Nulty.
 
 Tests
 -----
 
-- Issue #25449: Added tests for OrderedDict subclasses.
+- bpo-25449: Added tests for OrderedDict subclasses.
 
-- Issue #25099: Make test_compileall not fail when an entry on sys.path cannot
+- bpo-25099: Make test_compileall not fail when an entry on sys.path cannot
   be written to (commonly seen in administrative installs on Windows).
 
-- Issue #23919: Prevents assert dialogs appearing in the test suite.
+- bpo-23919: Prevents assert dialogs appearing in the test suite.
 
-- ``PCbuild\rt.bat`` now accepts an unlimited number of arguments to pass along
-  to regrtest.py.  Previously there was a limit of 9.
+- ``PCbuild\rt.bat`` now accepts an unlimited number of arguments to pass
+  along to regrtest.py.  Previously there was a limit of 9.
 
 Build
 -----
 
-- Issue #24915: Add LLVM support for PGO builds and use the test suite to
+- bpo-24915: Add LLVM support for PGO builds and use the test suite to
   generate the profile data. Initial patch by Alecsandru Patrascu of Intel.
 
-- Issue #24910: Windows MSIs now have unique display names.
+- bpo-24910: Windows MSIs now have unique display names.
 
-- Issue #24986: It is now possible to build Python on Windows without errors
+- bpo-24986: It is now possible to build Python on Windows without errors
   when external libraries are not available.
 
 Windows
 -------
 
-- Issue #25450: Updates shortcuts to start Python in installation directory.
+- bpo-25450: Updates shortcuts to start Python in installation directory.
 
-- Issue #25164: Changes default all-users install directory to match per-user
+- bpo-25164: Changes default all-users install directory to match per-user
   directory.
 
-- Issue #25143: Improves installer error messages for unsupported platforms.
+- bpo-25143: Improves installer error messages for unsupported platforms.
 
-- Issue #25163: Display correct directory in installer when using non-default
+- bpo-25163: Display correct directory in installer when using non-default
   settings.
 
-- Issue #25361: Disables use of SSE2 instructions in Windows 32-bit build
+- bpo-25361: Disables use of SSE2 instructions in Windows 32-bit build
 
-- Issue #25089: Adds logging to installer for case where launcher is not
+- bpo-25089: Adds logging to installer for case where launcher is not
   selected on upgrade.
 
-- Issue #25165: Windows uninstallation should not remove launcher if other
+- bpo-25165: Windows uninstallation should not remove launcher if other
   versions remain
 
-- Issue #25112: py.exe launcher is missing icons
+- bpo-25112: py.exe launcher is missing icons
 
-- Issue #25102: Windows installer does not precompile for -O or -OO.
+- bpo-25102: Windows installer does not precompile for -O or -OO.
 
-- Issue #25081: Makes Back button in installer go back to upgrade page when
+- bpo-25081: Makes Back button in installer go back to upgrade page when
   upgrading.
 
-- Issue #25091: Increases font size of the installer.
+- bpo-25091: Increases font size of the installer.
 
-- Issue #25126: Clarifies that the non-web installer will download some
+- bpo-25126: Clarifies that the non-web installer will download some
   components.
 
-- Issue #25213: Restores requestedExecutionLevel to manifest to disable
-  UAC virtualization.
+- bpo-25213: Restores requestedExecutionLevel to manifest to disable UAC
+  virtualization.
 
-- Issue #25022: Removed very outdated PC/example_nt/ directory.
+- bpo-25022: Removed very outdated PC/example_nt/ directory.
 
 Tools/Demos
 -----------
 
-- Issue #25440: Fix output of python-config --extension-suffix.
+- bpo-25440: Fix output of python-config --extension-suffix.
 
 
 What's New in Python 3.5.0 final?
 =================================
 
-Release date: 2015-09-13
+*Release date: 2015-09-13*
 
 Build
 -----
 
-- Issue #25071: Windows installer should not require TargetDir
-  parameter when installing quietly.
+- bpo-25071: Windows installer should not require TargetDir parameter when
+  installing quietly.
 
 
 What's New in Python 3.5.0 release candidate 4?
 ===============================================
 
-Release date: 2015-09-09
+*Release date: 2015-09-09*
 
 Library
 -------
 
-- Issue #25029: Fixes MemoryError in test_strptime.
+- bpo-25029: Fixes MemoryError in test_strptime.
 
 Build
 -----
 
-- Issue #25027: Reverts partial-static build options and adds
-  vcruntime140.dll to Windows installation.
+- bpo-25027: Reverts partial-static build options and adds vcruntime140.dll
+  to Windows installation.
 
 
 What's New in Python 3.5.0 release candidate 3?
 ===============================================
 
-Release date: 2015-09-07
+*Release date: 2015-09-07*
 
 Core and Builtins
 -----------------
 
-- Issue #24305: Prevent import subsystem stack frames from being counted
-  by the warnings.warn(stacklevel=) parameter.
+- bpo-24305: Prevent import subsystem stack frames from being counted by the
+  warnings.warn(stacklevel=) parameter.
 
-- Issue #24912: Prevent __class__ assignment to immutable built-in objects.
+- bpo-24912: Prevent __class__ assignment to immutable built-in objects.
 
-- Issue #24975: Fix AST compilation for PEP 448 syntax.
+- bpo-24975: Fix AST compilation for PEP 448 syntax.
 
 Library
 -------
 
-- Issue #24917: time_strftime() buffer over-read.
+- bpo-24917: time_strftime() buffer over-read.
 
-- Issue #24748: To resolve a compatibility problem found with py2exe and
+- bpo-24748: To resolve a compatibility problem found with py2exe and
   pywin32, imp.load_dynamic() once again ignores previously loaded modules
   to support Python modules replacing themselves with extension modules.
   Patch by Petr Viktorin.
 
-- Issue #24635: Fixed a bug in typing.py where isinstance([], typing.Iterable)
+- bpo-24635: Fixed a bug in typing.py where isinstance([], typing.Iterable)
   would return True once, then False on subsequent calls.
 
-- Issue #24989: Fixed buffer overread in BytesIO.readline() if a position is
+- bpo-24989: Fixed buffer overread in BytesIO.readline() if a position is
   set beyond size.  Based on patch by John Leitch.
 
-- Issue #24913: Fix overrun error in deque.index().
-  Found by John Leitch and Bryce Darling.
+- bpo-24913: Fix overrun error in deque.index(). Found by John Leitch and
+  Bryce Darling.
 
 
 What's New in Python 3.5.0 release candidate 2?
 ===============================================
 
-Release date: 2015-08-25
+*Release date: 2015-08-25*
 
 Core and Builtins
 -----------------
 
-- Issue #24769: Interpreter now starts properly when dynamic loading
-  is disabled.  Patch by Petr Viktorin.
+- bpo-24769: Interpreter now starts properly when dynamic loading is
+  disabled.  Patch by Petr Viktorin.
 
-- Issue #21167: NAN operations are now handled correctly when python is
+- bpo-21167: NAN operations are now handled correctly when python is
   compiled with ICC even if -fp-model strict is not specified.
 
-- Issue #24492: A "package" lacking a __name__ attribute when trying to perform
-  a ``from .. import ...`` statement will trigger an ImportError instead of an
-  AttributeError.
+- bpo-24492: A "package" lacking a __name__ attribute when trying to perform
+  a ``from .. import ...`` statement will trigger an ImportError instead of
+  an AttributeError.
 
 Library
 -------
 
-- Issue #24847: Removes vcruntime140.dll dependency from Tcl/Tk.
+- bpo-24847: Removes vcruntime140.dll dependency from Tcl/Tk.
 
-- Issue #24839: platform._syscmd_ver raises DeprecationWarning
+- bpo-24839: platform._syscmd_ver raises DeprecationWarning
 
-- Issue #24867: Fix Task.get_stack() for 'async def' coroutines
+- bpo-24867: Fix Task.get_stack() for 'async def' coroutines
 
 
 What's New in Python 3.5.0 release candidate 1?
 ===============================================
 
-Release date: 2015-08-09
+*Release date: 2015-08-09*
 
 Core and Builtins
 -----------------
 
-- Issue #24667: Resize odict in all cases that the underlying dict resizes.
+- bpo-24667: Resize odict in all cases that the underlying dict resizes.
 
 Library
 -------
 
-- Issue #24824: Signatures of codecs.encode() and codecs.decode() now are
+- bpo-24824: Signatures of codecs.encode() and codecs.decode() now are
   compatible with pydoc.
 
-- Issue #24634: Importing uuid should not try to load libc on Windows
+- bpo-24634: Importing uuid should not try to load libc on Windows
 
-- Issue #24798: _msvccompiler.py doesn't properly support manifests
+- bpo-24798: _msvccompiler.py doesn't properly support manifests
 
-- Issue #4395: Better testing and documentation of binary operators.
-  Patch by Martin Panter.
+- bpo-4395: Better testing and documentation of binary operators. Patch by
+  Martin Panter.
 
-- Issue #23973: Update typing.py from GitHub repo.
+- bpo-23973: Update typing.py from GitHub repo.
 
-- Issue #23004: mock_open() now reads binary data correctly when the type of
+- bpo-23004: mock_open() now reads binary data correctly when the type of
   read_data is bytes.  Initial patch by Aaron Hill.
 
-- Issue #23888: Handle fractional time in cookie expiry. Patch by ssh.
+- bpo-23888: Handle fractional time in cookie expiry. Patch by ssh.
 
-- Issue #23652: Make it possible to compile the select module against the
-  libc headers from the Linux Standard Base, which do not include some
-  EPOLL macros.  Patch by Matt Frank.
+- bpo-23652: Make it possible to compile the select module against the libc
+  headers from the Linux Standard Base, which do not include some EPOLL
+  macros.  Patch by Matt Frank.
 
-- Issue #22932: Fix timezones in email.utils.formatdate.
-  Patch from Dmitry Shachnev.
+- bpo-22932: Fix timezones in email.utils.formatdate. Patch from Dmitry
+  Shachnev.
 
-- Issue #23779: imaplib raises TypeError if authenticator tries to abort.
-  Patch from Craig Holmquist.
+- bpo-23779: imaplib raises TypeError if authenticator tries to abort. Patch
+  from Craig Holmquist.
 
-- Issue #23319: Fix ctypes.BigEndianStructure, swap correctly bytes. Patch
+- bpo-23319: Fix ctypes.BigEndianStructure, swap correctly bytes. Patch
   written by Matthieu Gautier.
 
-- Issue #23254: Document how to close the TCPServer listening socket.
-  Patch from Martin Panter.
+- bpo-23254: Document how to close the TCPServer listening socket. Patch
+  from Martin Panter.
 
-- Issue #19450: Update Windows and OS X installer builds to use SQLite 3.8.11.
+- bpo-19450: Update Windows and OS X installer builds to use SQLite 3.8.11.
 
-- Issue #17527: Add PATCH to wsgiref.validator. Patch from Luca Sbardella.
+- bpo-17527: Add PATCH to wsgiref.validator. Patch from Luca Sbardella.
 
-- Issue #24791: Fix grammar regression for call syntax: 'g(\*a or b)'.
+- bpo-24791: Fix grammar regression for call syntax: 'g(\*a or b)'.
 
 IDLE
 ----
 
-- Issue #23672: Allow Idle to edit and run files with astral chars in name.
+- bpo-23672: Allow Idle to edit and run files with astral chars in name.
   Patch by Mohd Sanad Zaki Rizvi.
 
-- Issue #24745: Idle editor default font. Switch from Courier to
-  platform-sensitive TkFixedFont.  This should not affect current customized
-  font selections.  If there is a problem, edit $HOME/.idlerc/config-main.cfg
-  and remove 'fontxxx' entries from [Editor Window].  Patch by Mark Roseman.
+- bpo-24745: Idle editor default font. Switch from Courier to platform-
+  sensitive TkFixedFont.  This should not affect current customized font
+  selections.  If there is a problem, edit $HOME/.idlerc/config-main.cfg and
+  remove 'fontxxx' entries from [Editor Window].  Patch by Mark Roseman.
 
-- Issue #21192: Idle editor. When a file is run, put its name in the restart bar.
-  Do not print false prompts. Original patch by Adnan Umer.
+- bpo-21192: Idle editor. When a file is run, put its name in the restart
+  bar. Do not print false prompts. Original patch by Adnan Umer.
 
-- Issue #13884: Idle menus. Remove tearoff lines. Patch by Roger Serwy.
+- bpo-13884: Idle menus. Remove tearoff lines. Patch by Roger Serwy.
 
 Documentation
 -------------
 
-- Issue #24129: Clarify the reference documentation for name resolution.
-  This includes removing the assumption that readers will be familiar with the
+- bpo-24129: Clarify the reference documentation for name resolution. This
+  includes removing the assumption that readers will be familiar with the
   name resolution scheme Python used prior to the introduction of lexical
   scoping for function namespaces. Patch by Ivan Levkivskyi.
 
-- Issue #20769: Improve reload() docs. Patch by Dorian Pula.
+- bpo-20769: Improve reload() docs. Patch by Dorian Pula.
 
-- Issue #23589: Remove duplicate sentence from the FAQ.  Patch by Yongzhi Pan.
+- bpo-23589: Remove duplicate sentence from the FAQ.  Patch by Yongzhi Pan.
 
-- Issue #24729: Correct IO tutorial to match implementation regarding
-  encoding parameter to open function.
+- bpo-24729: Correct IO tutorial to match implementation regarding encoding
+  parameter to open function.
 
 Tests
 -----
 
-- Issue #24751: When running regrtest with the ``-w`` command line option,
-  a test run is no longer marked as a failure if all tests succeed when
-  re-run.
+- bpo-24751: When running regrtest with the ``-w`` command line option, a
+  test run is no longer marked as a failure if all tests succeed when re-
+  run.
 
 
 What's New in Python 3.5.0 beta 4?
 ==================================
 
-Release date: 2015-07-26
+*Release date: 2015-07-26*
 
 Core and Builtins
 -----------------
 
-- Issue #23573: Restored optimization of bytes.rfind() and bytearray.rfind()
+- bpo-23573: Restored optimization of bytes.rfind() and bytearray.rfind()
   for single-byte argument on Linux.
 
-- Issue #24569: Make PEP 448 dictionary evaluation more consistent.
+- bpo-24569: Make PEP 448 dictionary evaluation more consistent.
 
-- Issue #24583: Fix crash when set is mutated while being updated.
+- bpo-24583: Fix crash when set is mutated while being updated.
 
-- Issue #24407: Fix crash when dict is mutated while being updated.
+- bpo-24407: Fix crash when dict is mutated while being updated.
 
-- Issue #24619: New approach for tokenizing async/await. As a consequence,
-  it is now possible to have one-line 'async def foo(): await ..' functions.
+- bpo-24619: New approach for tokenizing async/await. As a consequence, it
+  is now possible to have one-line 'async def foo(): await ..' functions.
 
-- Issue #24687: Plug refleak on SyntaxError in function parameters
-  annotations.
+- bpo-24687: Plug refleak on SyntaxError in function parameters annotations.
 
-- Issue #15944: memoryview: Allow arbitrary formats when casting to bytes.
+- bpo-15944: memoryview: Allow arbitrary formats when casting to bytes.
   Patch by Martin Panter.
 
 Library
 -------
 
-- Issue #23441: rcompleter now prints a tab character instead of displaying
+- bpo-23441: rcompleter now prints a tab character instead of displaying
   possible completions for an empty word.  Initial patch by Martin Sekera.
 
-- Issue #24683: Fixed crashes in _json functions called with arguments of
+- bpo-24683: Fixed crashes in _json functions called with arguments of
   inappropriate type.
 
-- Issue #21697: shutil.copytree() now correctly handles symbolic links that
+- bpo-21697: shutil.copytree() now correctly handles symbolic links that
   point to directories.  Patch by Eduardo Seabra and Thomas Kluyver.
 
-- Issue #14373: Fixed segmentation fault when gc.collect() is called during
+- bpo-14373: Fixed segmentation fault when gc.collect() is called during
   constructing lru_cache (C implementation).
 
-- Issue #24695: Fix a regression in traceback.print_exception().  If
+- bpo-24695: Fix a regression in traceback.print_exception().  If
   exc_traceback is None we shouldn't print a traceback header like described
   in the documentation.
 
-- Issue #24620: Random.setstate() now validates the value of state last element.
+- bpo-24620: Random.setstate() now validates the value of state last
+  element.
 
-- Issue #22485: Fixed an issue that caused `inspect.getsource` to return
+- bpo-22485: Fixed an issue that caused `inspect.getsource` to return
   incorrect results on nested functions.
 
-- Issue #22153: Improve unittest docs. Patch from Martin Panter and evilzero.
+- bpo-22153: Improve unittest docs. Patch from Martin Panter and evilzero.
 
-- Issue #24580: Symbolic group references to open group in re patterns now are
+- bpo-24580: Symbolic group references to open group in re patterns now are
   explicitly forbidden as well as numeric group references.
 
-- Issue #24206: Fixed __eq__ and __ne__ methods of inspect classes.
+- bpo-24206: Fixed __eq__ and __ne__ methods of inspect classes.
 
-- Issue #24631: Fixed regression in the timeit module with multiline setup.
+- bpo-24631: Fixed regression in the timeit module with multiline setup.
 
-- Issue #18622: unittest.mock.mock_open().reset_mock would recurse infinitely.
+- bpo-18622: unittest.mock.mock_open().reset_mock would recurse infinitely.
   Patch from Nicola Palumbo and Laurent De Buyst.
 
-- Issue #23661: unittest.mock side_effects can now be exceptions again. This
+- bpo-23661: unittest.mock side_effects can now be exceptions again. This
   was a regression vs Python 3.4. Patch from Ignacio Rossi
 
-- Issue #24608: chunk.Chunk.read() now always returns bytes, not str.
+- bpo-24608: chunk.Chunk.read() now always returns bytes, not str.
 
-- Issue #18684: Fixed reading out of the buffer in the re module.
+- bpo-18684: Fixed reading out of the buffer in the re module.
 
-- Issue #24259: tarfile now raises a ReadError if an archive is truncated
+- bpo-24259: tarfile now raises a ReadError if an archive is truncated
   inside a data segment.
 
-- Issue #15014: SMTP.auth() and SMTP.login() now support RFC 4954's optional
-  initial-response argument to the SMTP AUTH command.
+- bpo-15014: SMTP.auth() and SMTP.login() now support RFC 4954's optional
+  initial- response argument to the SMTP AUTH command.
 
-- Issue #24669: Fix inspect.getsource() for 'async def' functions.
-  Patch by Kai Groner.
+- bpo-24669: Fix inspect.getsource() for 'async def' functions. Patch by Kai
+  Groner.
 
-- Issue #24688: ast.get_docstring() for 'async def' functions.
+- bpo-24688: ast.get_docstring() for 'async def' functions.
 
 Build
 -----
 
-- Issue #24603: Update Windows builds and OS X 10.5 installer to use OpenSSL
+- bpo-24603: Update Windows builds and OS X 10.5 installer to use OpenSSL
   1.0.2d.
 
 
 What's New in Python 3.5.0 beta 3?
 ==================================
 
-Release date: 2015-07-05
+*Release date: 2015-07-05*
 
 Core and Builtins
 -----------------
 
-- Issue #24467: Fixed possible buffer over-read in bytearray. The bytearray
-  object now always allocates place for trailing null byte and it's buffer now
-  is always null-terminated.
+- bpo-24467: Fixed possible buffer over-read in bytearray. The bytearray
+  object now always allocates place for trailing null byte and it's buffer
+  now is always null-terminated.
 
 - Upgrade to Unicode 8.0.0.
 
-- Issue #24345: Add Py_tp_finalize slot for the stable ABI.
+- bpo-24345: Add Py_tp_finalize slot for the stable ABI.
 
-- Issue #24400: Introduce a distinct type for PEP 492 coroutines; add
-  types.CoroutineType, inspect.getcoroutinestate, inspect.getcoroutinelocals;
-  coroutines no longer use CO_GENERATOR flag; sys.set_coroutine_wrapper
-  works only for 'async def' coroutines; inspect.iscoroutine no longer
-  uses collections.abc.Coroutine, it's intended to test for pure 'async def'
-  coroutines only; add new opcode: GET_YIELD_FROM_ITER; fix generators wrapper
-  used in types.coroutine to be instance of collections.abc.Generator;
-  collections.abc.Awaitable and collections.abc.Coroutine can no longer
-  be used to detect generator-based coroutines--use inspect.isawaitable
-  instead.
+- bpo-24400: Introduce a distinct type for PEP 492 coroutines; add
+  types.CoroutineType, inspect.getcoroutinestate,
+  inspect.getcoroutinelocals; coroutines no longer use CO_GENERATOR flag;
+  sys.set_coroutine_wrapper works only for 'async def' coroutines;
+  inspect.iscoroutine no longer uses collections.abc.Coroutine, it's
+  intended to test for pure 'async def' coroutines only; add new opcode:
+  GET_YIELD_FROM_ITER; fix generators wrapper used in types.coroutine to be
+  instance of collections.abc.Generator; collections.abc.Awaitable and
+  collections.abc.Coroutine can no longer be used to detect generator-based
+  coroutines--use inspect.isawaitable instead.
 
-- Issue #24450: Add gi_yieldfrom to generators and cr_await to coroutines.
+- bpo-24450: Add gi_yieldfrom to generators and cr_await to coroutines.
   Contributed by Benno Leslie and Yury Selivanov.
 
-- Issue #19235: Add new RecursionError exception. Patch by Georg Brandl.
+- bpo-19235: Add new RecursionError exception. Patch by Georg Brandl.
 
 Library
 -------
 
-- Issue #21750: mock_open.read_data can now be read from each instance, as it
+- bpo-21750: mock_open.read_data can now be read from each instance, as it
   could in Python 3.3.
 
-- Issue #24552: Fix use after free in an error case of the _pickle module.
+- bpo-24552: Fix use after free in an error case of the _pickle module.
 
-- Issue #24514: tarfile now tolerates number fields consisting of only
+- bpo-24514: tarfile now tolerates number fields consisting of only
   whitespace.
 
-- Issue #19176: Fixed doctype() related bugs in C implementation of ElementTree.
-  A deprecation warning no longer issued by XMLParser subclass with default
-  doctype() method.  Direct call of doctype() now issues a warning.  Parser's
-  doctype() now is not called if target's doctype() is called.  Based on patch
-  by Martin Panter.
+- bpo-19176: Fixed doctype() related bugs in C implementation of
+  ElementTree. A deprecation warning no longer issued by XMLParser subclass
+  with default doctype() method.  Direct call of doctype() now issues a
+  warning.  Parser's doctype() now is not called if target's doctype() is
+  called.  Based on patch by Martin Panter.
 
-- Issue #20387: Restore semantic round-trip correctness in tokenize/untokenize
-  for tab-indented blocks.
+- bpo-20387: Restore semantic round-trip correctness in tokenize/untokenize
+  for tab- indented blocks.
 
-- Issue #24456: Fixed possible buffer over-read in adpcm2lin() and lin2adpcm()
+- bpo-24456: Fixed possible buffer over-read in adpcm2lin() and lin2adpcm()
   functions of the audioop module.
 
-- Issue #24336: The contextmanager decorator now works with functions with
+- bpo-24336: The contextmanager decorator now works with functions with
   keyword arguments called "func" and "self".  Patch by Martin Panter.
 
-- Issue #24522: Fix possible integer overflow in json accelerator module.
+- bpo-24522: Fix possible integer overflow in json accelerator module.
 
-- Issue #24489: ensure a previously set C errno doesn't disturb cmath.polar().
+- bpo-24489: ensure a previously set C errno doesn't disturb cmath.polar().
 
-- Issue #24408: Fixed AttributeError in measure() and metrics() methods of
+- bpo-24408: Fixed AttributeError in measure() and metrics() methods of
   tkinter.Font.
 
-- Issue #14373: C implementation of functools.lru_cache() now can be used with
+- bpo-14373: C implementation of functools.lru_cache() now can be used with
   methods.
 
-- Issue #24347: Set KeyError if PyDict_GetItemWithError returns NULL.
+- bpo-24347: Set KeyError if PyDict_GetItemWithError returns NULL.
 
-- Issue #24348: Drop superfluous incref/decref.
+- bpo-24348: Drop superfluous incref/decref.
 
-- Issue #24359: Check for changed OrderedDict size during iteration.
+- bpo-24359: Check for changed OrderedDict size during iteration.
 
-- Issue #24368: Support keyword arguments in OrderedDict methods.
+- bpo-24368: Support keyword arguments in OrderedDict methods.
 
-- Issue #24362: Simplify the C OrderedDict fast nodes resize logic.
+- bpo-24362: Simplify the C OrderedDict fast nodes resize logic.
 
-- Issue #24377: Fix a ref leak in OrderedDict.__repr__.
+- bpo-24377: Fix a ref leak in OrderedDict.__repr__.
 
-- Issue #24369: Defend against key-changes during iteration.
+- bpo-24369: Defend against key-changes during iteration.
 
 Tests
 -----
 
-- Issue #24373: _testmultiphase and xxlimited now use tp_traverse and
+- bpo-24373: _testmultiphase and xxlimited now use tp_traverse and
   tp_finalize to avoid reference leaks encountered when combining tp_dealloc
   with PyType_FromSpec (see issue #16690 for details)
 
 Documentation
 -------------
 
-- Issue #24458: Update documentation to cover multi-phase initialization for
+- bpo-24458: Update documentation to cover multi-phase initialization for
   extension modules (PEP 489). Patch by Petr Viktorin.
 
-- Issue #24351: Clarify what is meant by "identifier" in the context of
+- bpo-24351: Clarify what is meant by "identifier" in the context of
   string.Template instances.
 
 Build
 -----
 
-- Issue #24432: Update Windows builds and OS X 10.5 installer to use OpenSSL
+- bpo-24432: Update Windows builds and OS X 10.5 installer to use OpenSSL
   1.0.2c.
 
 
 What's New in Python 3.5.0 beta 2?
 ==================================
 
-Release date: 2015-05-31
+*Release date: 2015-05-31*
 
 Core and Builtins
 -----------------
 
-- Issue #24284: The startswith and endswith methods of the str class no longer
-  return True when finding the empty string and the indexes are completely out
-  of range.
+- bpo-24284: The startswith and endswith methods of the str class no longer
+  return True when finding the empty string and the indexes are completely
+  out of range.
 
-- Issue #24115: Update uses of PyObject_IsTrue(), PyObject_Not(),
+- bpo-24115: Update uses of PyObject_IsTrue(), PyObject_Not(),
   PyObject_IsInstance(), PyObject_RichCompareBool() and _PyDict_Contains()
   to check for and handle errors correctly.
 
-- Issue #24328: Fix importing one character extension modules.
+- bpo-24328: Fix importing one character extension modules.
 
-- Issue #11205: In dictionary displays, evaluate the key before the value.
+- bpo-11205: In dictionary displays, evaluate the key before the value.
 
-- Issue #24285: Fixed regression that prevented importing extension modules
+- bpo-24285: Fixed regression that prevented importing extension modules
   from inside packages. Patch by Petr Viktorin.
 
 Library
 -------
 
-- Issue #23247: Fix a crash in the StreamWriter.reset() of CJK codecs.
+- bpo-23247: Fix a crash in the StreamWriter.reset() of CJK codecs.
 
-- Issue #24270: Add math.isclose() and cmath.isclose() functions as per PEP 485.
-  Contributed by Chris Barker and Tal Einat.
+- bpo-24270: Add math.isclose() and cmath.isclose() functions as per PEP
+  485. Contributed by Chris Barker and Tal Einat.
 
-- Issue #5633: Fixed timeit when the statement is a string and the setup is not.
+- bpo-5633: Fixed timeit when the statement is a string and the setup is
+  not.
 
-- Issue #24326: Fixed audioop.ratecv() with non-default weightB argument.
+- bpo-24326: Fixed audioop.ratecv() with non-default weightB argument.
   Original patch by David Moore.
 
-- Issue #16991: Add a C implementation of OrderedDict.
+- bpo-16991: Add a C implementation of OrderedDict.
 
-- Issue #23934: Fix inspect.signature to fail correctly for builtin types
+- bpo-23934: Fix inspect.signature to fail correctly for builtin types
   lacking signature information.  Initial patch by James Powell.
 
 
 What's New in Python 3.5.0 beta 1?
 ==================================
 
-Release date: 2015-05-24
+*Release date: 2015-05-24*
 
 Core and Builtins
 -----------------
 
-- Issue #24276: Fixed optimization of property descriptor getter.
+- bpo-24276: Fixed optimization of property descriptor getter.
 
-- Issue #24268: PEP 489: Multi-phase extension module initialization.
-  Patch by Petr Viktorin.
+- bpo-24268: PEP 489: Multi-phase extension module initialization. Patch by
+  Petr Viktorin.
 
-- Issue #23955: Add pyvenv.cfg option to suppress registry/environment
-  lookup for generating sys.path on Windows.
+- bpo-23955: Add pyvenv.cfg option to suppress registry/environment lookup
+  for generating sys.path on Windows.
 
-- Issue #24257: Fixed system error in the comparison of faked
+- bpo-24257: Fixed system error in the comparison of faked
   types.SimpleNamespace.
 
-- Issue #22939: Fixed integer overflow in iterator object.  Patch by
-  Clement Rouault.
+- bpo-22939: Fixed integer overflow in iterator object.  Patch by Clement
+  Rouault.
 
-- Issue #23985: Fix a possible buffer overrun when deleting a slice from
-  the front of a bytearray and then appending some other bytes data.
+- bpo-23985: Fix a possible buffer overrun when deleting a slice from the
+  front of a bytearray and then appending some other bytes data.
 
-- Issue #24102: Fixed exception type checking in standard error handlers.
+- bpo-24102: Fixed exception type checking in standard error handlers.
 
-- Issue #15027: The UTF-32 encoder is now 3x to 7x faster.
+- bpo-15027: The UTF-32 encoder is now 3x to 7x faster.
 
-- Issue #23290: Optimize set_merge() for cases where the target is empty.
+- bpo-23290: Optimize set_merge() for cases where the target is empty.
   (Contributed by Serhiy Storchaka.)
 
-- Issue #2292: PEP 448: Additional Unpacking Generalizations.
+- bpo-2292: PEP 448: Additional Unpacking Generalizations.
 
-- Issue #24096: Make warnings.warn_explicit more robust against mutation of the
+- bpo-24096: Make warnings.warn_explicit more robust against mutation of the
   warnings.filters list.
 
-- Issue #23996: Avoid a crash when a delegated generator raises an
-  unnormalized StopIteration exception.  Patch by Stefan Behnel.
+- bpo-23996: Avoid a crash when a delegated generator raises an unnormalized
+  StopIteration exception.  Patch by Stefan Behnel.
 
-- Issue #23910: Optimize property() getter calls.  Patch by Joe Jevnik.
+- bpo-23910: Optimize property() getter calls.  Patch by Joe Jevnik.
 
-- Issue #23911: Move path-based importlib bootstrap code to a separate
-  frozen module.
+- bpo-23911: Move path-based importlib bootstrap code to a separate frozen
+  module.
 
-- Issue #24192: Fix namespace package imports.
+- bpo-24192: Fix namespace package imports.
 
-- Issue #24022: Fix tokenizer crash when processing undecodable source code.
+- bpo-24022: Fix tokenizer crash when processing undecodable source code.
 
-- Issue #9951: Added a hex() method to bytes, bytearray, and memoryview.
+- bpo-9951: Added a hex() method to bytes, bytearray, and memoryview.
 
-- Issue #22906: PEP 479: Change StopIteration handling inside generators.
+- bpo-22906: PEP 479: Change StopIteration handling inside generators.
 
-- Issue #24017: PEP 492: Coroutines with async and await syntax.
+- bpo-24017: PEP 492: Coroutines with async and await syntax.
 
 Library
 -------
 
-- Issue #14373: Added C implementation of functools.lru_cache().  Based on
+- bpo-14373: Added C implementation of functools.lru_cache().  Based on
   patches by Matt Joiner and Alexey Kachayev.
 
-- Issue #24230: The tempfile module now accepts bytes for prefix, suffix and dir
-  parameters and returns bytes in such situations (matching the os module APIs).
+- bpo-24230: The tempfile module now accepts bytes for prefix, suffix and
+  dir parameters and returns bytes in such situations (matching the os
+  module APIs).
 
-- Issue #22189: collections.UserString now supports __getnewargs__(),
+- bpo-22189: collections.UserString now supports __getnewargs__(),
   __rmod__(), casefold(), format_map(), isprintable(), and maketrans().
   Patch by Joe Jevnik.
 
-- Issue #24244: Prevents termination when an invalid format string is
+- bpo-24244: Prevents termination when an invalid format string is
   encountered on Windows in strftime.
 
-- Issue #23973: PEP 484: Add the typing module.
+- bpo-23973: PEP 484: Add the typing module.
 
-- Issue #23086: The collections.abc.Sequence() abstract base class added
-  *start* and *stop* parameters to the index() mixin.
-  Patch by Devin Jeanpierre.
+- bpo-23086: The collections.abc.Sequence() abstract base class added
+  *start* and *stop* parameters to the index() mixin. Patch by Devin
+  Jeanpierre.
 
-- Issue #20035: Replaced the ``tkinter._fix`` module used for setting up the
+- bpo-20035: Replaced the ``tkinter._fix`` module used for setting up the
   Tcl/Tk environment on Windows with a private function in the ``_tkinter``
   module that makes no permanent changes to the environment.
 
-- Issue #24257: Fixed segmentation fault in sqlite3.Row constructor with faked
+- bpo-24257: Fixed segmentation fault in sqlite3.Row constructor with faked
   cursor type.
 
-- Issue #15836: assertRaises(), assertRaisesRegex(), assertWarns() and
-  assertWarnsRegex() assertments now check the type of the first argument
-  to prevent possible user error.  Based on patch by Daniel Wagner-Hall.
+- bpo-15836: assertRaises(), assertRaisesRegex(), assertWarns() and
+  assertWarnsRegex() assertments now check the type of the first argument to
+  prevent possible user error.  Based on patch by Daniel Wagner-Hall.
 
-- Issue #9858: Add missing method stubs to _io.RawIOBase.  Patch by Laura
+- bpo-9858: Add missing method stubs to _io.RawIOBase.  Patch by Laura
   Rupprecht.
 
-- Issue #22955: attrgetter, itemgetter and methodcaller objects in the operator
+- bpo-22955: attrgetter, itemgetter and methodcaller objects in the operator
   module now support pickling.  Added readable and evaluable repr for these
-  objects.  Based on patch by Josh Rosenberg.
+  objects. Based on patch by Josh Rosenberg.
 
-- Issue #22107: tempfile.gettempdir() and tempfile.mkdtemp() now try again
-  when a directory with the chosen name already exists on Windows as well as
-  on Unix.  tempfile.mkstemp() now fails early if parent directory is not
-  valid (not exists or is a file) on Windows.
+- bpo-22107: tempfile.gettempdir() and tempfile.mkdtemp() now try again when
+  a directory with the chosen name already exists on Windows as well as on
+  Unix. tempfile.mkstemp() now fails early if parent directory is not valid
+  (not exists or is a file) on Windows.
 
-- Issue #23780: Improved error message in os.path.join() with single argument.
+- bpo-23780: Improved error message in os.path.join() with single argument.
 
-- Issue #6598: Increased time precision and random number range in
+- bpo-6598: Increased time precision and random number range in
   email.utils.make_msgid() to strengthen the uniqueness of the message ID.
 
-- Issue #24091: Fixed various crashes in corner cases in C implementation of
+- bpo-24091: Fixed various crashes in corner cases in C implementation of
   ElementTree.
 
-- Issue #21931: msilib.FCICreate() now raises TypeError in the case of a bad
-  argument instead of a ValueError with a bogus FCI error number.
-  Patch by Jeffrey Armstrong.
+- bpo-21931: msilib.FCICreate() now raises TypeError in the case of a bad
+  argument instead of a ValueError with a bogus FCI error number. Patch by
+  Jeffrey Armstrong.
 
-- Issue #13866: *quote_via* argument added to urllib.parse.urlencode.
+- bpo-13866: *quote_via* argument added to urllib.parse.urlencode.
 
-- Issue #20098: New mangle_from policy option for email, default True
-  for compat32, but False for all other policies.
+- bpo-20098: New mangle_from policy option for email, default True for
+  compat32, but False for all other policies.
 
-- Issue #24211: The email library now supports RFC 6532: it can generate
+- bpo-24211: The email library now supports RFC 6532: it can generate
   headers using utf-8 instead of encoded words.
 
-- Issue #16314: Added support for the LZMA compression in distutils.
+- bpo-16314: Added support for the LZMA compression in distutils.
 
-- Issue #21804: poplib now supports RFC 6856 (UTF8).
+- bpo-21804: poplib now supports RFC 6856 (UTF8).
 
-- Issue #18682: Optimized pprint functions for builtin scalar types.
+- bpo-18682: Optimized pprint functions for builtin scalar types.
 
-- Issue #22027: smtplib now supports RFC 6531 (SMTPUTF8).
+- bpo-22027: smtplib now supports RFC 6531 (SMTPUTF8).
 
-- Issue #23488: Random generator objects now consume 2x less memory on 64-bit.
+- bpo-23488: Random generator objects now consume 2x less memory on 64-bit.
 
-- Issue #1322: platform.dist() and platform.linux_distribution() functions are
+- bpo-1322: platform.dist() and platform.linux_distribution() functions are
   now deprecated.  Initial patch by Vajrasky Kok.
 
-- Issue #22486: Added the math.gcd() function.  The fractions.gcd() function
+- bpo-22486: Added the math.gcd() function.  The fractions.gcd() function
   now is deprecated.  Based on patch by Mark Dickinson.
 
-- Issue #24064: Property() docstrings are now writeable.
-  (Patch by Berker Peksag.)
+- bpo-24064: Property() docstrings are now writeable. (Patch by Berker
+  Peksag.)
 
-- Issue #22681: Added support for the koi8_t encoding.
+- bpo-22681: Added support for the koi8_t encoding.
 
-- Issue #22682: Added support for the kz1048 encoding.
+- bpo-22682: Added support for the kz1048 encoding.
 
-- Issue #23796: peek and read1 methods of BufferedReader now raise ValueError
+- bpo-23796: peek and read1 methods of BufferedReader now raise ValueError
   if they called on a closed object. Patch by John Hergenroeder.
 
-- Issue #21795: smtpd now supports the 8BITMIME extension whenever
-  the new *decode_data* constructor argument is set to False.
+- bpo-21795: smtpd now supports the 8BITMIME extension whenever the new
+  *decode_data* constructor argument is set to False.
 
-- Issue #24155: optimize heapq.heapify() for better cache performance
-  when heapifying large lists.
+- bpo-24155: optimize heapq.heapify() for better cache performance when
+  heapifying large lists.
 
-- Issue #21800: imaplib now supports RFC 5161 (enable), RFC 6855
+- bpo-21800: imaplib now supports RFC 5161 (enable), RFC 6855
   (utf8/internationalized email) and automatically encodes non-ASCII
   usernames and passwords to UTF8.
 
-- Issue #20274: When calling a _sqlite.Connection, it now complains if passed
+- bpo-20274: When calling a _sqlite.Connection, it now complains if passed
   any keyword arguments.  Previously it silently ignored them.
 
-- Issue #20274: Remove ignored and erroneous "kwargs" parameters from three
+- bpo-20274: Remove ignored and erroneous "kwargs" parameters from three
   METH_VARARGS methods on _sqlite.Connection.
 
-- Issue #24134: assertRaises(), assertRaisesRegex(), assertWarns() and
+- bpo-24134: assertRaises(), assertRaisesRegex(), assertWarns() and
   assertWarnsRegex() checks now emits a deprecation warning when callable is
-  None or keyword arguments except msg is passed in the context manager mode.
+  None or keyword arguments except msg is passed in the context manager
+  mode.
 
-- Issue #24018: Add a collections.abc.Generator abstract base class.
+- bpo-24018: Add a collections.abc.Generator abstract base class.
   Contributed by Stefan Behnel.
 
-- Issue #23880: Tkinter's getint() and getdouble() now support Tcl_Obj.
+- bpo-23880: Tkinter's getint() and getdouble() now support Tcl_Obj.
   Tkinter's getdouble() now supports any numbers (in particular int).
 
-- Issue #22619: Added negative limit support in the traceback module.
-  Based on patch by Dmitry Kazakov.
+- bpo-22619: Added negative limit support in the traceback module. Based on
+  patch by Dmitry Kazakov.
 
-- Issue #24094: Fix possible crash in json.encode with poorly behaved dict
+- bpo-24094: Fix possible crash in json.encode with poorly behaved dict
   subclasses.
 
-- Issue #9246: On POSIX, os.getcwd() now supports paths longer than 1025 bytes.
+- bpo-9246: On POSIX, os.getcwd() now supports paths longer than 1025 bytes.
   Patch written by William Orr.
 
-- Issue #17445: add difflib.diff_bytes() to support comparison of
-  byte strings (fixes a regression from Python 2).
+- bpo-17445: add difflib.diff_bytes() to support comparison of byte strings
+  (fixes a regression from Python 2).
 
-- Issue #23917: Fall back to sequential compilation when ProcessPoolExecutor
-  doesn't exist.  Patch by Claudiu Popa.
+- bpo-23917: Fall back to sequential compilation when ProcessPoolExecutor
+  doesn't exist. Patch by Claudiu Popa.
 
-- Issue #23008: Fixed resolving attributes with boolean value is False in pydoc.
+- bpo-23008: Fixed resolving attributes with boolean value is False in
+  pydoc.
 
-- Fix asyncio issue 235: LifoQueue and PriorityQueue's put didn't
-  increment unfinished tasks (this bug was introduced when
-  JoinableQueue was merged with Queue).
+- Fix asyncio issue 235: LifoQueue and PriorityQueue's put didn't increment
+  unfinished tasks (this bug was introduced when JoinableQueue was merged
+  with Queue).
 
-- Issue #23908: os functions now reject paths with embedded null character
-  on Windows instead of silently truncating them.
+- bpo-23908: os functions now reject paths with embedded null character on
+  Windows instead of silently truncating them.
 
-- Issue #23728: binascii.crc_hqx() could return an integer outside of the range
+- bpo-23728: binascii.crc_hqx() could return an integer outside of the range
   0-0xffff for empty data.
 
-- Issue #23887: urllib.error.HTTPError now has a proper repr() representation.
+- bpo-23887: urllib.error.HTTPError now has a proper repr() representation.
   Patch by Berker Peksag.
 
 - asyncio: New event loop APIs: set_task_factory() and get_task_factory().
 
 - asyncio: async() function is deprecated in favour of ensure_future().
 
-- Issue #24178: asyncio.Lock, Condition, Semaphore, and BoundedSemaphore
+- bpo-24178: asyncio.Lock, Condition, Semaphore, and BoundedSemaphore
   support new 'async with' syntax.  Contributed by Yury Selivanov.
 
-- Issue #24179: Support 'async for' for asyncio.StreamReader.
-  Contributed by Yury Selivanov.
-
-- Issue #24184: Add AsyncIterator and AsyncIterable ABCs to
-  collections.abc.  Contributed by Yury Selivanov.
+- bpo-24179: Support 'async for' for asyncio.StreamReader. Contributed by
+  Yury Selivanov.
 
-- Issue #22547: Implement informative __repr__ for inspect.BoundArguments.
+- bpo-24184: Add AsyncIterator and AsyncIterable ABCs to collections.abc.
   Contributed by Yury Selivanov.
 
-- Issue #24190: Implement inspect.BoundArgument.apply_defaults() method.
+- bpo-22547: Implement informative __repr__ for inspect.BoundArguments.
   Contributed by Yury Selivanov.
 
-- Issue #20691: Add 'follow_wrapped' argument to
-  inspect.Signature.from_callable() and inspect.signature().
+- bpo-24190: Implement inspect.BoundArgument.apply_defaults() method.
   Contributed by Yury Selivanov.
 
-- Issue #24248: Deprecate inspect.Signature.from_function() and
+- bpo-20691: Add 'follow_wrapped' argument to
+  inspect.Signature.from_callable() and inspect.signature(). Contributed by
+  Yury Selivanov.
+
+- bpo-24248: Deprecate inspect.Signature.from_function() and
   inspect.Signature.from_builtin().
 
-- Issue #23898: Fix inspect.classify_class_attrs() to support attributes
-  with overloaded __eq__ and __bool__.  Patch by Mike Bayer.
+- bpo-23898: Fix inspect.classify_class_attrs() to support attributes with
+  overloaded __eq__ and __bool__.  Patch by Mike Bayer.
 
-- Issue #24298: Fix inspect.signature() to correctly unwrap wrappers
-  around bound methods.
+- bpo-24298: Fix inspect.signature() to correctly unwrap wrappers around
+  bound methods.
 
 IDLE
 ----
 
-- Issue #23184: remove unused names and imports in idlelib.
-  Initial patch by Al Sweigart.
+- bpo-23184: remove unused names and imports in idlelib. Initial patch by Al
+  Sweigart.
 
 Tests
 -----
 
-- Issue #21520: test_zipfile no longer fails if the word 'bad' appears
-  anywhere in the name of the current directory.
+- bpo-21520: test_zipfile no longer fails if the word 'bad' appears anywhere
+  in the name of the current directory.
 
-- Issue #9517: Move script_helper into the support package.
-  Patch by Christie Wilson.
+- bpo-9517: Move script_helper into the support package. Patch by Christie
+  Wilson.
 
 Documentation
 -------------
 
-- Issue #22155: Add File Handlers subsection with createfilehandler to tkinter
+- bpo-22155: Add File Handlers subsection with createfilehandler to tkinter
   doc.  Remove obsolete example from FAQ.  Patch by Martin Panter.
 
-- Issue #24029: Document the name binding behavior for submodule imports.
+- bpo-24029: Document the name binding behavior for submodule imports.
 
-- Issue #24077: Fix typo in man page for -I command option: -s, not -S
+- bpo-24077: Fix typo in man page for -I command option: -s, not -S
 
 Tools/Demos
 -----------
 
-- Issue #24000: Improved Argument Clinic's mapping of converters to legacy
-  "format units".  Updated the documentation to match.
+- bpo-24000: Improved Argument Clinic's mapping of converters to legacy
+  "format units". Updated the documentation to match.
 
-- Issue #24001: Argument Clinic converters now use accept={type}
-  instead of types={'type'} to specify the types the converter accepts.
+- bpo-24001: Argument Clinic converters now use accept={type} instead of
+  types={'type'} to specify the types the converter accepts.
 
-- Issue #23330: h2py now supports arbitrary filenames in #include.
+- bpo-23330: h2py now supports arbitrary filenames in #include.
 
-- Issue #24031: make patchcheck now supports git checkouts, too.
+- bpo-24031: make patchcheck now supports git checkouts, too.
 
 
 What's New in Python 3.5.0 alpha 4?
 ===================================
 
-Release date: 2015-04-19
+*Release date: 2015-04-19*
 
 Core and Builtins
 -----------------
 
-- Issue #22980: Under Linux, GNU/KFreeBSD and the Hurd, C extensions now include
-  the architecture triplet in the extension name, to make it easy to test builds
-  for different ABIs in the same working tree.  Under OS X, the extension name
-  now includes PEP 3149-style information.
+- bpo-22980: Under Linux, GNU/KFreeBSD and the Hurd, C extensions now
+  include the architecture triplet in the extension name, to make it easy to
+  test builds for different ABIs in the same working tree.  Under OS X, the
+  extension name now includes PEP 3149-style information.
 
-- Issue #22631: Added Linux-specific socket constant CAN_RAW_FD_FRAMES.
-  Patch courtesy of Joe Jevnik.
+- bpo-22631: Added Linux-specific socket constant CAN_RAW_FD_FRAMES. Patch
+  courtesy of Joe Jevnik.
 
-- Issue #23731: Implement PEP 488: removal of .pyo files.
+- bpo-23731: Implement PEP 488: removal of .pyo files.
 
-- Issue #23726: Don't enable GC for user subclasses of non-GC types that
-  don't add any new fields.  Patch by Eugene Toder.
+- bpo-23726: Don't enable GC for user subclasses of non-GC types that don't
+  add any new fields.  Patch by Eugene Toder.
 
-- Issue #23309: Avoid a deadlock at shutdown if a daemon thread is aborted
+- bpo-23309: Avoid a deadlock at shutdown if a daemon thread is aborted
   while it is holding a lock to a buffered I/O object, and the main thread
   tries to use the same I/O object (typically stdout or stderr).  A fatal
   error is emitted instead.
 
-- Issue #22977: Fixed formatting Windows error messages on Wine.
-  Patch by Martin Panter.
+- bpo-22977: Fixed formatting Windows error messages on Wine. Patch by
+  Martin Panter.
 
-- Issue #23466: %c, %o, %x, and %X in bytes formatting now raise TypeError on
+- bpo-23466: %c, %o, %x, and %X in bytes formatting now raise TypeError on
   non-integer input.
 
-- Issue #24044: Fix possible null pointer dereference in list.sort in out of
+- bpo-24044: Fix possible null pointer dereference in list.sort in out of
   memory conditions.
 
-- Issue #21354: PyCFunction_New function is exposed by python DLL again.
+- bpo-21354: PyCFunction_New function is exposed by python DLL again.
 
 Library
 -------
 
-- Issue #23840: tokenize.open() now closes the temporary binary file on error
+- bpo-23840: tokenize.open() now closes the temporary binary file on error
   to fix a resource warning.
 
-- Issue #16914: new debuglevel 2 in smtplib adds timestamps to debug output.
+- bpo-16914: new debuglevel 2 in smtplib adds timestamps to debug output.
 
-- Issue #7159: urllib.request now supports sending auth credentials
+- bpo-7159: urllib.request now supports sending auth credentials
   automatically after the first 401.  This enhancement is a superset of the
   enhancement from issue #19494 and supersedes that change.
 
-- Issue #23703: Fix a regression in urljoin() introduced in 901e4e52b20a.
-  Patch by Demian Brecht.
+- bpo-23703: Fix a regression in urljoin() introduced in 901e4e52b20a. Patch
+  by Demian Brecht.
 
-- Issue #4254: Adds _curses.update_lines_cols().  Patch by Arnon Yaari
+- bpo-4254: Adds _curses.update_lines_cols().  Patch by Arnon Yaari
 
-- Issue #19933: Provide default argument for ndigits in round. Patch by
+- bpo-19933: Provide default argument for ndigits in round. Patch by
   Vajrasky Kok.
 
-- Issue #23193: Add a numeric_owner parameter to
-  tarfile.TarFile.extract and tarfile.TarFile.extractall. Patch by
-  Michael Vogt and Eric Smith.
+- bpo-23193: Add a numeric_owner parameter to tarfile.TarFile.extract and
+  tarfile.TarFile.extractall. Patch by Michael Vogt and Eric Smith.
 
-- Issue #23342: Add a subprocess.run() function than returns a CalledProcess
+- bpo-23342: Add a subprocess.run() function than returns a CalledProcess
   instance for a more consistent API than the existing call* functions.
 
-- Issue #21217: inspect.getsourcelines() now tries to compute the start and end
-  lines from the code object, fixing an issue when a lambda function is used as
-  decorator argument. Patch by Thomas Ballinger and Allison Kaptur.
+- bpo-21217: inspect.getsourcelines() now tries to compute the start and end
+  lines from the code object, fixing an issue when a lambda function is used
+  as decorator argument. Patch by Thomas Ballinger and Allison Kaptur.
 
-- Issue #24521: Fix possible integer overflows in the pickle module.
+- bpo-24521: Fix possible integer overflows in the pickle module.
 
-- Issue #22931: Allow '[' and ']' in cookie values.
+- bpo-22931: Allow '[' and ']' in cookie values.
 
 - The keywords attribute of functools.partial is now always a dictionary.
 
-- Issue #23811: Add missing newline to the PyCompileError error message.
-  Patch by Alex Shkop.
+- bpo-23811: Add missing newline to the PyCompileError error message. Patch
+  by Alex Shkop.
 
-- Issue #21116: Avoid blowing memory when allocating a multiprocessing shared
-  array that's larger than 50% of the available RAM.  Patch by Médéric Boquien.
+- bpo-21116: Avoid blowing memory when allocating a multiprocessing shared
+  array that's larger than 50% of the available RAM.  Patch by Médéric
+  Boquien.
 
-- Issue #22982: Improve BOM handling when seeking to multiple positions of
-  writable text file.
+- bpo-22982: Improve BOM handling when seeking to multiple positions of a
+  writable text file.
 
-- Issue #23464: Removed deprecated asyncio JoinableQueue.
+- bpo-23464: Removed deprecated asyncio JoinableQueue.
 
-- Issue #23529: Limit the size of decompressed data when reading from
-  GzipFile, BZ2File or LZMAFile.  This defeats denial of service attacks
-  using compressed bombs (i.e. compressed payloads which decompress to a huge
+- bpo-23529: Limit the size of decompressed data when reading from GzipFile,
+  BZ2File or LZMAFile.  This defeats denial of service attacks using
+  compressed bombs (i.e. compressed payloads which decompress to a huge
   size).  Patch by Martin Panter and Nikolaus Rath.
 
-- Issue #21859: Added Python implementation of io.FileIO.
+- bpo-21859: Added Python implementation of io.FileIO.
 
-- Issue #23865: close() methods in multiple modules now are idempotent and more
+- bpo-23865: close() methods in multiple modules now are idempotent and more
   robust at shutdown. If they need to release multiple resources, all are
   released even if errors occur.
 
-- Issue #23400: Raise same exception on both Python 2 and 3 if sem_open is not
-  available.  Patch by Davin Potts.
+- bpo-23400: Raise same exception on both Python 2 and 3 if sem_open is not
+  available. Patch by Davin Potts.
 
-- Issue #10838: The subprocess now module includes SubprocessError and
-  TimeoutError in its list of exported names for the users wild enough
-  to use ``from subprocess import *``.
+- bpo-10838: The subprocess now module includes SubprocessError and
+  TimeoutError in its list of exported names for the users wild enough to
+  use ``from subprocess import *``.
 
-- Issue #23411: Added DefragResult, ParseResult, SplitResult, DefragResultBytes,
-  ParseResultBytes, and SplitResultBytes to urllib.parse.__all__.
-  Patch by Martin Panter.
+- bpo-23411: Added DefragResult, ParseResult, SplitResult,
+  DefragResultBytes, ParseResultBytes, and SplitResultBytes to
+  urllib.parse.__all__. Patch by Martin Panter.
 
-- Issue #23881: urllib.request.ftpwrapper constructor now closes the socket if
+- bpo-23881: urllib.request.ftpwrapper constructor now closes the socket if
   the FTP connection failed to fix a ResourceWarning.
 
-- Issue #23853: :meth:`socket.socket.sendall` does no more reset the socket
+- bpo-23853: :meth:`socket.socket.sendall` does no more reset the socket
   timeout each time data is sent successfully. The socket timeout is now the
   maximum total duration to send all data.
 
-- Issue #22721: An order of multiline pprint output of set or dict containing
-  orderable and non-orderable elements no longer depends on iteration order of
-  set or dict.
+- bpo-22721: An order of multiline pprint output of set or dict containing
+  orderable and non-orderable elements no longer depends on iteration order
+  of set or dict.
 
-- Issue #15133: _tkinter.tkapp.getboolean() now supports Tcl_Obj and always
-  returns bool.  tkinter.BooleanVar now validates input values (accepted bool,
-  int, str, and Tcl_Obj).  tkinter.BooleanVar.get() now always returns bool.
+- bpo-15133: _tkinter.tkapp.getboolean() now supports Tcl_Obj and always
+  returns bool. tkinter.BooleanVar now validates input values (accepted
+  bool, int, str, and Tcl_Obj).  tkinter.BooleanVar.get() now always returns
+  bool.
 
-- Issue #10590: xml.sax.parseString() now supports string argument.
+- bpo-10590: xml.sax.parseString() now supports string argument.
 
-- Issue #23338: Fixed formatting ctypes error messages on Cygwin.
-  Patch by Makoto Kato.
+- bpo-23338: Fixed formatting ctypes error messages on Cygwin. Patch by
+  Makoto Kato.
 
-- Issue #15582: inspect.getdoc() now follows inheritance chains.
+- bpo-15582: inspect.getdoc() now follows inheritance chains.
 
-- Issue #2175: SAX parsers now support a character stream of InputSource object.
+- bpo-2175: SAX parsers now support a character stream of InputSource
+  object.
 
-- Issue #16840: Tkinter now supports 64-bit integers added in Tcl 8.4 and
+- bpo-16840: Tkinter now supports 64-bit integers added in Tcl 8.4 and
   arbitrary precision integers added in Tcl 8.5.
 
-- Issue #23834: Fix socket.sendto(), use the C Py_ssize_t type to store the
+- bpo-23834: Fix socket.sendto(), use the C Py_ssize_t type to store the
   result of sendto() instead of the C int type.
 
-- Issue #23618: :meth:`socket.socket.connect` now waits until the connection
+- bpo-23618: :meth:`socket.socket.connect` now waits until the connection
   completes instead of raising :exc:`InterruptedError` if the connection is
   interrupted by signals, signal handlers don't raise an exception and the
   socket is blocking or has a timeout. :meth:`socket.socket.connect` still
   raise :exc:`InterruptedError` for non-blocking sockets.
 
-- Issue #21526: Tkinter now supports new boolean type in Tcl 8.5.
+- bpo-21526: Tkinter now supports new boolean type in Tcl 8.5.
 
-- Issue #23836: Fix the faulthandler module to handle reentrant calls to
-  its signal handlers.
+- bpo-23836: Fix the faulthandler module to handle reentrant calls to its
+  signal handlers.
 
-- Issue #23838: linecache now clears the cache and returns an empty result on
+- bpo-23838: linecache now clears the cache and returns an empty result on
   MemoryError.
 
-- Issue #10395: Added os.path.commonpath(). Implemented in posixpath and ntpath.
-  Based on patch by Rafik Draoui.
+- bpo-10395: Added os.path.commonpath(). Implemented in posixpath and
+  ntpath. Based on patch by Rafik Draoui.
 
-- Issue #23611: Serializing more "lookupable" objects (such as unbound methods
+- bpo-23611: Serializing more "lookupable" objects (such as unbound methods
   or nested classes) now are supported with pickle protocols < 4.
 
-- Issue #13583: sqlite3.Row now supports slice indexing.
+- bpo-13583: sqlite3.Row now supports slice indexing.
 
-- Issue #18473: Fixed 2to3 and 3to2 compatible pickle mappings.  Fixed
-  ambigious reverse mappings.  Added many new mappings.  Import mapping is no
-  longer applied to modules already mapped with full name mapping.
+- bpo-18473: Fixed 2to3 and 3to2 compatible pickle mappings.  Fixed
+  ambigious reverse mappings.  Added many new mappings.  Import mapping is
+  no longer applied to modules already mapped with full name mapping.
 
-- Issue #23485: select.select() is now retried automatically with the
-  recomputed timeout when interrupted by a signal, except if the signal handler
-  raises an exception. This change is part of the PEP 475.
+- bpo-23485: select.select() is now retried automatically with the
+  recomputed timeout when interrupted by a signal, except if the signal
+  handler raises an exception. This change is part of the PEP 475.
 
-- Issue #23752: When built from an existing file descriptor, io.FileIO() now
+- bpo-23752: When built from an existing file descriptor, io.FileIO() now
   only calls fstat() once. Before fstat() was called twice, which was not
   necessary.
 
-- Issue #23704: collections.deque() objects now support __add__, __mul__, and
+- bpo-23704: collections.deque() objects now support __add__, __mul__, and
   __imul__().
 
-- Issue #23171: csv.Writer.writerow() now supports arbitrary iterables.
+- bpo-23171: csv.Writer.writerow() now supports arbitrary iterables.
 
-- Issue #23745: The new email header parser now handles duplicate MIME
+- bpo-23745: The new email header parser now handles duplicate MIME
   parameter names without error, similar to how get_param behaves.
 
-- Issue #22117: Fix os.utime(), it now rounds the timestamp towards minus
+- bpo-22117: Fix os.utime(), it now rounds the timestamp towards minus
   infinity (-inf) instead of rounding towards zero.
 
-- Issue #23310: Fix MagicMock's initializer to work with __methods__, just
-  like configure_mock().  Patch by Kasia Jachim.
+- bpo-23310: Fix MagicMock's initializer to work with __methods__, just like
+  configure_mock().  Patch by Kasia Jachim.
 
 Build
 -----
 
-- Issue #23817: FreeBSD now uses "1.0" in the SOVERSION as other operating
+- bpo-23817: FreeBSD now uses "1.0" in the SOVERSION as other operating
   systems, instead of just "1".
 
-- Issue #23501: Argument Clinic now generates code into separate files by default.
+- bpo-23501: Argument Clinic now generates code into separate files by
+  default.
 
 Tests
 -----
 
-- Issue #23799: Added test.support.start_threads() for running and
-  cleaning up multiple threads.
+- bpo-23799: Added test.support.start_threads() for running and cleaning up
+  multiple threads.
 
-- Issue #22390: test.regrtest now emits a warning if temporary files or
+- bpo-22390: test.regrtest now emits a warning if temporary files or
   directories are left after running a test.
 
 Tools/Demos
 -----------
 
-- Issue #18128: pygettext now uses standard +NNNN format in the
-  POT-Creation-Date header.
+- bpo-18128: pygettext now uses standard +NNNN format in the POT-Creation-
+  Date header.
 
-- Issue #23935: Argument Clinic's understanding of format units
-  accepting bytes, bytearrays, and buffers is now consistent with
-  both the documentation and the implementation.
+- bpo-23935: Argument Clinic's understanding of format units accepting
+  bytes, bytearrays, and buffers is now consistent with both the
+  documentation and the implementation.
 
-- Issue #23944: Argument Clinic now wraps long impl prototypes at column 78.
+- bpo-23944: Argument Clinic now wraps long impl prototypes at column 78.
 
-- Issue #20586: Argument Clinic now ensures that functions without docstrings
+- bpo-20586: Argument Clinic now ensures that functions without docstrings
   have signatures.
 
-- Issue #23492: Argument Clinic now generates argument parsing code with
+- bpo-23492: Argument Clinic now generates argument parsing code with
   PyArg_Parse instead of PyArg_ParseTuple if possible.
 
-- Issue #23500: Argument Clinic is now smarter about generating the "#ifndef"
+- bpo-23500: Argument Clinic is now smarter about generating the "#ifndef"
   (empty) definition of the methoddef macro: it's only generated once, even
-  if Argument Clinic processes the same symbol multiple times, and it's emitted
-  at the end of all processing rather than immediately after the first use.
+  if Argument Clinic processes the same symbol multiple times, and it's
+  emitted at the end of all processing rather than immediately after the
+  first use.
 
 C API
 -----
 
-- Issue #23998: PyImport_ReInitLock() now checks for lock allocation error
+- bpo-23998: PyImport_ReInitLock() now checks for lock allocation error
 
 
 What's New in Python 3.5.0 alpha 3?
 ===================================
 
-Release date: 2015-03-28
+*Release date: 2015-03-28*
 
 Core and Builtins
 -----------------
 
-- Issue #23573: Increased performance of string search operations (str.find,
+- bpo-23573: Increased performance of string search operations (str.find,
   str.index, str.count, the in operator, str.split, str.partition) with
   arguments of different kinds (UCS1, UCS2, UCS4).
 
-- Issue #23753: Python doesn't support anymore platforms without stat() or
+- bpo-23753: Python doesn't support anymore platforms without stat() or
   fstat(), these functions are always required.
 
-- Issue #23681: The -b option now affects comparisons of bytes with int.
+- bpo-23681: The -b option now affects comparisons of bytes with int.
 
-- Issue #23632: Memoryviews now allow tuple indexing (including for
-  multi-dimensional memoryviews).
+- bpo-23632: Memoryviews now allow tuple indexing (including for multi-
+  dimensional memoryviews).
 
-- Issue #23192: Fixed generator lambdas.  Patch by Bruno Cauet.
+- bpo-23192: Fixed generator lambdas.  Patch by Bruno Cauet.
 
-- Issue #23629: Fix the default __sizeof__ implementation for variable-sized
+- bpo-23629: Fix the default __sizeof__ implementation for variable-sized
   objects.
 
 Library
 -------
 
-- Issue #14260: The groupindex attribute of regular expression pattern object
-  now is non-modifiable mapping.
+- bpo-14260: The groupindex attribute of regular expression pattern object
+  now is non- modifiable mapping.
 
-- Issue #23792: Ignore KeyboardInterrupt when the pydoc pager is active.
-  This mimics the behavior of the standard unix pagers, and prevents
-  pipepager from shutting down while the pager itself is still running.
+- bpo-23792: Ignore KeyboardInterrupt when the pydoc pager is active. This
+  mimics the behavior of the standard unix pagers, and prevents pipepager
+  from shutting down while the pager itself is still running.
 
-- Issue #23775: pprint() of OrderedDict now outputs the same representation
-  as repr().
+- bpo-23775: pprint() of OrderedDict now outputs the same representation as
+  repr().
 
-- Issue #23765: Removed IsBadStringPtr calls in ctypes
+- bpo-23765: Removed IsBadStringPtr calls in ctypes
 
-- Issue #22364: Improved some re error messages using regex for hints.
+- bpo-22364: Improved some re error messages using regex for hints.
 
-- Issue #23742: ntpath.expandvars() no longer loses unbalanced single quotes.
+- bpo-23742: ntpath.expandvars() no longer loses unbalanced single quotes.
 
-- Issue #21717: The zipfile.ZipFile.open function now supports 'x' (exclusive
+- bpo-21717: The zipfile.ZipFile.open function now supports 'x' (exclusive
   creation) mode.
 
-- Issue #21802: The reader in BufferedRWPair now is closed even when closing
+- bpo-21802: The reader in BufferedRWPair now is closed even when closing
   writer failed in BufferedRWPair.close().
 
-- Issue #23622: Unknown escapes in regular expressions that consist of ``'\'``
+- bpo-23622: Unknown escapes in regular expressions that consist of ``'\'``
   and ASCII letter now raise a deprecation warning and will be forbidden in
   Python 3.6.
 
-- Issue #23671: string.Template now allows specifying the "self" parameter as
-  keyword argument.  string.Formatter now allows specifying the "self" and
+- bpo-23671: string.Template now allows specifying the "self" parameter as a
+  keyword argument.  string.Formatter now allows specifying the "self" and
   the "format_string" parameters as keyword arguments.
 
-- Issue #23502: The pprint module now supports mapping proxies.
+- bpo-23502: The pprint module now supports mapping proxies.
 
-- Issue #17530: pprint now wraps long bytes objects and bytearrays.
+- bpo-17530: pprint now wraps long bytes objects and bytearrays.
 
-- Issue #22687: Fixed some corner cases in breaking words in tetxtwrap.
-  Got rid of quadratic complexity in breaking long words.
+- bpo-22687: Fixed some corner cases in breaking words in tetxtwrap. Got rid
+  of quadratic complexity in breaking long words.
 
-- Issue #4727: The copy module now uses pickle protocol 4 (PEP 3154) and
+- bpo-4727: The copy module now uses pickle protocol 4 (PEP 3154) and
   supports copying of instances of classes whose __new__ method takes
   keyword-only arguments.
 
-- Issue #23491: Added a zipapp module to support creating executable zip
-  file archives of Python code. Registered ".pyz" and ".pyzw" extensions
-  on Windows for these archives (PEP 441).
+- bpo-23491: Added a zipapp module to support creating executable zip file
+  archives of Python code. Registered ".pyz" and ".pyzw" extensions on
+  Windows for these archives (PEP 441).
 
-- Issue #23657: Avoid explicit checks for str in zipapp, adding support
-  for pathlib.Path objects as arguments.
+- bpo-23657: Avoid explicit checks for str in zipapp, adding support for
+  pathlib.Path objects as arguments.
 
-- Issue #23688: Added support of arbitrary bytes-like objects and avoided
-  unnecessary copying of memoryview in gzip.GzipFile.write().
-  Original patch by Wolfgang Maier.
+- bpo-23688: Added support of arbitrary bytes-like objects and avoided
+  unnecessary copying of memoryview in gzip.GzipFile.write(). Original patch
+  by Wolfgang Maier.
 
-- Issue #23252: Added support for writing ZIP files to unseekable streams.
+- bpo-23252: Added support for writing ZIP files to unseekable streams.
 
-- Issue #23647: Increase impalib's MAXLINE to accommodate modern mailbox sizes.
+- bpo-23647: Increase impalib's MAXLINE to accommodate modern mailbox sizes.
 
-- Issue #23539: If body is None, http.client.HTTPConnection.request now sets
-  Content-Length to 0 for PUT, POST, and PATCH headers to avoid 411 errors from
-  some web servers.
+- bpo-23539: If body is None, http.client.HTTPConnection.request now sets
+  Content-Length to 0 for PUT, POST, and PATCH headers to avoid 411 errors
+  from some web servers.
 
-- Issue #22351: The nntplib.NNTP constructor no longer leaves the connection
+- bpo-22351: The nntplib.NNTP constructor no longer leaves the connection
   and socket open until the garbage collector cleans them up.  Patch by
   Martin Panter.
 
-- Issue #23704: collections.deque() objects now support methods for index(),
+- bpo-23704: collections.deque() objects now support methods for index(),
   insert(), and copy().  This allows deques to be registered as a
   MutableSequence and it improves their substitutability for lists.
 
-- Issue #23715: :func:`signal.sigwaitinfo` and :func:`signal.sigtimedwait` are
+- bpo-23715: :func:`signal.sigwaitinfo` and :func:`signal.sigtimedwait` are
   now retried when interrupted by a signal not in the *sigset* parameter, if
   the signal handler does not raise an exception. signal.sigtimedwait()
   recomputes the timeout with a monotonic clock when it is retried.
 
-- Issue #23001: Few functions in modules mmap, ossaudiodev, socket, ssl, and
+- bpo-23001: Few functions in modules mmap, ossaudiodev, socket, ssl, and
   codecs, that accepted only read-only bytes-like object now accept writable
   bytes-like object too.
 
-- Issue #23646: If time.sleep() is interrupted by a signal, the sleep is now
+- bpo-23646: If time.sleep() is interrupted by a signal, the sleep is now
   retried with the recomputed delay, except if the signal handler raises an
   exception (PEP 475).
 
-- Issue #23136: _strptime now uniformly handles all days in week 0, including
+- bpo-23136: _strptime now uniformly handles all days in week 0, including
   Dec 30 of previous year.  Based on patch by Jim Carroll.
 
-- Issue #23700: Iterator of NamedTemporaryFile now keeps a reference to
+- bpo-23700: Iterator of NamedTemporaryFile now keeps a reference to
   NamedTemporaryFile instance.  Patch by Bohuslav Kabrda.
 
-- Issue #22903: The fake test case created by unittest.loader when it fails
+- bpo-22903: The fake test case created by unittest.loader when it fails
   importing a test module is now picklable.
 
-- Issue #22181: On Linux, os.urandom() now uses the new getrandom() syscall if
-  available, syscall introduced in the Linux kernel 3.17. It is more reliable
-  and more secure, because it avoids the need of a file descriptor and waits
-  until the kernel has enough entropy.
+- bpo-22181: On Linux, os.urandom() now uses the new getrandom() syscall if
+  available, syscall introduced in the Linux kernel 3.17. It is more
+  reliable and more secure, because it avoids the need of a file descriptor
+  and waits until the kernel has enough entropy.
 
-- Issue #2211: Updated the implementation of the http.cookies.Morsel class.
+- bpo-2211: Updated the implementation of the http.cookies.Morsel class.
   Setting attributes key, value and coded_value directly now is deprecated.
   update() and setdefault() now transform and check keys.  Comparing for
   equality now takes into account attributes key, value and coded_value.
-  copy() now returns a Morsel, not a dict.  repr() now contains all attributes.
-  Optimized checking keys and quoting values.  Added new tests.
+  copy() now returns a Morsel, not a dict.  repr() now contains all
+  attributes. Optimized checking keys and quoting values.  Added new tests.
   Original patch by Demian Brecht.
 
-- Issue #18983: Allow selection of output units in timeit.
-  Patch by Julian Gindi.
+- bpo-18983: Allow selection of output units in timeit. Patch by Julian
+  Gindi.
 
-- Issue #23631: Fix traceback.format_list when a traceback has been mutated.
+- bpo-23631: Fix traceback.format_list when a traceback has been mutated.
 
-- Issue #23568: Add rdivmod support to MagicMock() objects.
-  Patch by Håkan Lövdahl.
+- bpo-23568: Add rdivmod support to MagicMock() objects. Patch by Håkan
+  Lövdahl.
 
-- Issue #2052: Add charset parameter to HtmlDiff.make_file().
+- bpo-2052: Add charset parameter to HtmlDiff.make_file().
 
-- Issue #23668: Support os.truncate and os.ftruncate on Windows.
+- bpo-23668: Support os.truncate and os.ftruncate on Windows.
 
-- Issue #23138: Fixed parsing cookies with absent keys or values in cookiejar.
+- bpo-23138: Fixed parsing cookies with absent keys or values in cookiejar.
   Patch by Demian Brecht.
 
-- Issue #23051: multiprocessing.Pool methods imap() and imap_unordered() now
+- bpo-23051: multiprocessing.Pool methods imap() and imap_unordered() now
   handle exceptions raised by an iterator.  Patch by Alon Diamant and Davin
   Potts.
 
-- Issue #23581: Add matmul support to MagicMock. Patch by Håkan Lövdahl.
+- bpo-23581: Add matmul support to MagicMock. Patch by Håkan Lövdahl.
 
-- Issue #23566: enable(), register(), dump_traceback() and
+- bpo-23566: enable(), register(), dump_traceback() and
   dump_traceback_later() functions of faulthandler now accept file
   descriptors. Patch by Wei Wu.
 
-- Issue #22928: Disabled HTTP header injections in http.client.
-  Original patch by Demian Brecht.
+- bpo-22928: Disabled HTTP header injections in http.client. Original patch
+  by Demian Brecht.
 
-- Issue #23615: Modules bz2, tarfile and tokenize now can be reloaded with
-  imp.reload().  Patch by Thomas Kluyver.
+- bpo-23615: Modules bz2, tarfile and tokenize now can be reloaded with
+  imp.reload(). Patch by Thomas Kluyver.
 
-- Issue #23605: os.walk() now calls os.scandir() instead of os.listdir().
-  The usage of os.scandir() reduces the number of calls to os.stat().
-  Initial patch written by Ben Hoyt.
+- bpo-23605: os.walk() now calls os.scandir() instead of os.listdir(). The
+  usage of os.scandir() reduces the number of calls to os.stat(). Initial
+  patch written by Ben Hoyt.
 
 Build
 -----
 
-- Issue #23585: make patchcheck will ensure the interpreter is built.
+- bpo-23585: make patchcheck will ensure the interpreter is built.
 
 Tests
 -----
 
-- Issue #23583: Added tests for standard IO streams in IDLE.
+- bpo-23583: Added tests for standard IO streams in IDLE.
 
-- Issue #22289: Prevent test_urllib2net failures due to ftp connection timeout.
+- bpo-22289: Prevent test_urllib2net failures due to ftp connection timeout.
 
 Tools/Demos
 -----------
 
-- Issue #22826: The result of open() in Tools/freeze/bkfile.py is now better
+- bpo-22826: The result of open() in Tools/freeze/bkfile.py is now better
   compatible with regular files (in particular it now supports the context
   management protocol).
 
 
-What's New in Python 3.5 alpha 2?
-=================================
+What's New in Python 3.5.0 alpha 2?
+===================================
 
-Release date: 2015-03-09
+*Release date: 2015-03-09*
 
 Core and Builtins
 -----------------
 
-- Issue #23571: PyObject_Call() and PyCFunction_Call() now raise a SystemError
+- bpo-23571: PyObject_Call() and PyCFunction_Call() now raise a SystemError
   if a function returns a result and raises an exception. The SystemError is
   chained to the previous exception.
 
 Library
 -------
 
-- Issue #22524: New os.scandir() function, part of the PEP 471: "os.scandir()
+- bpo-22524: New os.scandir() function, part of the PEP 471: "os.scandir()
   function -- a better and faster directory iterator". Patch written by Ben
   Hoyt.
 
-- Issue #23103: Reduced the memory consumption of IPv4Address and IPv6Address.
+- bpo-23103: Reduced the memory consumption of IPv4Address and IPv6Address.
 
-- Issue #21793: BaseHTTPRequestHandler again logs response code as numeric,
-  not as stringified enum.  Patch by Demian Brecht.
+- bpo-21793: BaseHTTPRequestHandler again logs response code as numeric, not
+  as stringified enum.  Patch by Demian Brecht.
 
-- Issue #23476: In the ssl module, enable OpenSSL's X509_V_FLAG_TRUSTED_FIRST
+- bpo-23476: In the ssl module, enable OpenSSL's X509_V_FLAG_TRUSTED_FIRST
   flag on certificate stores when it is available.
 
-- Issue #23576: Avoid stalling in SSL reads when EOF has been reached in the
+- bpo-23576: Avoid stalling in SSL reads when EOF has been reached in the
   SSL layer but the underlying connection hasn't been closed.
 
-- Issue #23504: Added an __all__ to the types module.
+- bpo-23504: Added an __all__ to the types module.
 
-- Issue #23563: Optimized utility functions in urllib.parse.
+- bpo-23563: Optimized utility functions in urllib.parse.
 
-- Issue #7830: Flatten nested functools.partial.
+- bpo-7830: Flatten nested functools.partial.
 
-- Issue #20204: Added the __module__ attribute to _tkinter classes.
+- bpo-20204: Added the __module__ attribute to _tkinter classes.
 
-- Issue #19980: Improved help() for non-recognized strings.  help('') now
-  shows the help on str.  help('help') now shows the help on help().
-  Original patch by Mark Lawrence.
+- bpo-19980: Improved help() for non-recognized strings.  help('') now shows
+  the help on str.  help('help') now shows the help on help(). Original
+  patch by Mark Lawrence.
 
-- Issue #23521: Corrected pure python implementation of timedelta division.
+- bpo-23521: Corrected pure python implementation of timedelta division.
 
* Eliminated OverflowError from ``timedelta * float`` for some floats;
* Corrected rounding in timedlta true division.
+  Eliminated OverflowError from ``timedelta * float`` for some floats;
+  Corrected rounding in timedlta true division.
 
-- Issue #21619: Popen objects no longer leave a zombie after exit in the with
+- bpo-21619: Popen objects no longer leave a zombie after exit in the with
   statement if the pipe was broken.  Patch by Martin Panter.
 
-- Issue #22936: Make it possible to show local variables in tracebacks for
-  both the traceback module and unittest.
+- bpo-22936: Make it possible to show local variables in tracebacks for both
+  the traceback module and unittest.
 
-- Issue #15955: Add an option to limit the output size in bz2.decompress().
+- bpo-15955: Add an option to limit the output size in bz2.decompress().
   Patch by Nikolaus Rath.
 
-- Issue #6639: Module-level turtle functions no longer raise TclError after
+- bpo-6639: Module-level turtle functions no longer raise TclError after
   closing the window.
 
-- Issues #814253, #9179: Group references and conditional group references now
-  work in lookbehind assertions in regular expressions.
+- bpo-814253: Group references and conditional group references now work in
+  lookbehind assertions in regular expressions. (See also: bpo-9179)
 
-- Issue #23215: Multibyte codecs with custom error handlers that ignores errors
-  consumed too much memory and raised SystemError or MemoryError.
-  Original patch by Aleksi Torhamo.
+- bpo-23215: Multibyte codecs with custom error handlers that ignores errors
+  consumed too much memory and raised SystemError or MemoryError. Original
+  patch by Aleksi Torhamo.
 
-- Issue #5700: io.FileIO() called flush() after closing the file.
-  flush() was not called in close() if closefd=False.
+- bpo-5700: io.FileIO() called flush() after closing the file. flush() was
+  not called in close() if closefd=False.
 
-- Issue #23374: Fixed pydoc failure with non-ASCII files when stdout encoding
+- bpo-23374: Fixed pydoc failure with non-ASCII files when stdout encoding
   differs from file system encoding (e.g. on Mac OS).
 
-- Issue #23481: Remove RC4 from the SSL module's default cipher list.
+- bpo-23481: Remove RC4 from the SSL module's default cipher list.
 
-- Issue #21548: Fix pydoc.synopsis() and pydoc.apropos() on modules with empty
+- bpo-21548: Fix pydoc.synopsis() and pydoc.apropos() on modules with empty
   docstrings.
 
-- Issue #22885: Fixed arbitrary code execution vulnerability in the dbm.dumb
-  module.  Original patch by Claudiu Popa.
+- bpo-22885: Fixed arbitrary code execution vulnerability in the dbm.dumb
+  module. Original patch by Claudiu Popa.
 
-- Issue #23239: ssl.match_hostname() now supports matching of IP addresses.
+- bpo-23239: ssl.match_hostname() now supports matching of IP addresses.
 
-- Issue #23146: Fix mishandling of absolute Windows paths with forward
-  slashes in pathlib.
+- bpo-23146: Fix mishandling of absolute Windows paths with forward slashes
+  in pathlib.
 
-- Issue #23096: Pickle representation of floats with protocol 0 now is the same
+- bpo-23096: Pickle representation of floats with protocol 0 now is the same
   for both Python and C implementations.
 
-- Issue #19105: pprint now more efficiently uses free space at the right.
+- bpo-19105: pprint now more efficiently uses free space at the right.
 
-- Issue #14910: Add allow_abbrev parameter to argparse.ArgumentParser. Patch by
+- bpo-14910: Add allow_abbrev parameter to argparse.ArgumentParser. Patch by
   Jonathan Paugh, Steven Bethard, paul j3 and Daniel Eriksson.
 
-- Issue #21717: tarfile.open() now supports 'x' (exclusive creation) mode.
+- bpo-21717: tarfile.open() now supports 'x' (exclusive creation) mode.
 
-- Issue #23344: marshal.dumps() is now 20-25% faster on average.
+- bpo-23344: marshal.dumps() is now 20-25% faster on average.
 
-- Issue #20416: marshal.dumps() with protocols 3 and 4 is now 40-50% faster on
+- bpo-20416: marshal.dumps() with protocols 3 and 4 is now 40-50% faster on
   average.
 
-- Issue #23421: Fixed compression in tarfile CLI.  Patch by wdv4758h.
+- bpo-23421: Fixed compression in tarfile CLI.  Patch by wdv4758h.
 
-- Issue #23367: Fix possible overflows in the unicodedata module.
+- bpo-23367: Fix possible overflows in the unicodedata module.
 
-- Issue #23361: Fix possible overflow in Windows subprocess creation code.
+- bpo-23361: Fix possible overflow in Windows subprocess creation code.
 
 - logging.handlers.QueueListener now takes a respect_handler_level keyword
-  argument which, if set to True, will pass messages to handlers taking handler
-  levels into account.
+  argument which, if set to True, will pass messages to handlers taking
+  handler levels into account.
 
-- Issue #19705: turtledemo now has a visual sorting algorithm demo.  Original
+- bpo-19705: turtledemo now has a visual sorting algorithm demo.  Original
   patch from Jason Yeo.
 
-- Issue #23801: Fix issue where cgi.FieldStorage did not always ignore the
+- bpo-23801: Fix issue where cgi.FieldStorage did not always ignore the
   entire preamble to a multipart body.
 
 Build
 -----
 
-- Issue #23445: pydebug builds now use "gcc -Og" where possible, to make
-  the resulting executable faster.
+- bpo-23445: pydebug builds now use "gcc -Og" where possible, to make the
+  resulting executable faster.
 
-- Issue #23686: Update OS X 10.5 installer build to use OpenSSL 1.0.2a.
+- bpo-23686: Update OS X 10.5 installer build to use OpenSSL 1.0.2a.
 
 C API
 -----
 
-- Issue #20204: Deprecation warning is now raised for builtin types without the
+- bpo-20204: Deprecation warning is now raised for builtin types without the
   __module__ attribute.
 
 Windows
 -------
 
-- Issue #23465: Implement PEP 486 - Make the Python Launcher aware of virtual
+- bpo-23465: Implement PEP 486 - Make the Python Launcher aware of virtual
   environments. Patch by Paul Moore.
 
-- Issue #23437: Make user scripts directory versioned on Windows. Patch by Paul
+- bpo-23437: Make user scripts directory versioned on Windows. Patch by Paul
   Moore.
 
 
-What's New in Python 3.5 alpha 1?
-=================================
+What's New in Python 3.5.0 alpha 1?
+===================================
 
-Release date: 2015-02-08
+*Release date: 2015-02-08*
 
 Core and Builtins
 -----------------
 
-- Issue #23285: PEP 475 - EINTR handling.
+- bpo-23285: PEP 475 - EINTR handling.
 
-- Issue #22735: Fix many edge cases (including crashes) involving custom mro()
+- bpo-22735: Fix many edge cases (including crashes) involving custom mro()
   implementations.
 
-- Issue #22896: Avoid using PyObject_AsCharBuffer(), PyObject_AsReadBuffer()
+- bpo-22896: Avoid using PyObject_AsCharBuffer(), PyObject_AsReadBuffer()
   and PyObject_AsWriteBuffer().
 
-- Issue #21295: Revert some changes (issue #16795) to AST line numbers and
+- bpo-21295: Revert some changes (issue #16795) to AST line numbers and
   column offsets that constituted a regression.
 
-- Issue #22986: Allow changing an object's __class__ between a dynamic type and
+- bpo-22986: Allow changing an object's __class__ between a dynamic type and
   static type in some cases.
 
-- Issue #15859: PyUnicode_EncodeFSDefault(), PyUnicode_EncodeMBCS() and
+- bpo-15859: PyUnicode_EncodeFSDefault(), PyUnicode_EncodeMBCS() and
   PyUnicode_EncodeCodePage() now raise an exception if the object is not a
-  Unicode object. For PyUnicode_EncodeFSDefault(), it was already the case on
-  platforms other than Windows. Patch written by Campbell Barton.
+  Unicode object. For PyUnicode_EncodeFSDefault(), it was already the case
+  on platforms other than Windows. Patch written by Campbell Barton.
 
-- Issue #21408: The default __ne__() now returns NotImplemented if __eq__()
+- bpo-21408: The default __ne__() now returns NotImplemented if __eq__()
   returned NotImplemented.  Original patch by Martin Panter.
 
-- Issue #23321: Fixed a crash in str.decode() when error handler returned
+- bpo-23321: Fixed a crash in str.decode() when error handler returned
   replacment string longer than mailformed input data.
 
-- Issue #22286: The "backslashreplace" error handlers now works with
-  decoding and translating.
+- bpo-22286: The "backslashreplace" error handlers now works with decoding
+  and translating.
 
-- Issue #23253: Delay-load ShellExecute[AW] in os.startfile for reduced
-  startup overhead on Windows.
+- bpo-23253: Delay-load ShellExecute[AW] in os.startfile for reduced startup
+  overhead on Windows.
 
-- Issue #22038: pyatomic.h now uses stdatomic.h or GCC built-in functions for
-  atomic memory access if available. Patch written by Vitor de Lima and Gustavo
-  Temple.
+- bpo-22038: pyatomic.h now uses stdatomic.h or GCC built-in functions for
+  atomic memory access if available. Patch written by Vitor de Lima and
+  Gustavo Temple.
 
-- Issue #20284: %-interpolation (aka printf) formatting added for bytes and
+- bpo-20284: %-interpolation (aka printf) formatting added for bytes and
   bytearray.
 
-- Issue #23048: Fix jumping out of an infinite while loop in the pdb.
+- bpo-23048: Fix jumping out of an infinite while loop in the pdb.
 
-- Issue #20335: bytes constructor now raises TypeError when encoding or errors
+- bpo-20335: bytes constructor now raises TypeError when encoding or errors
   is specified with non-string argument.  Based on patch by Renaud Blanch.
 
-- Issue #22834: If the current working directory ends up being set to a
-  non-existent directory then import will no longer raise FileNotFoundError.
+- bpo-22834: If the current working directory ends up being set to a non-
+  existent directory then import will no longer raise FileNotFoundError.
 
-- Issue #22869: Move the interpreter startup & shutdown code to a new
-  dedicated pylifecycle.c module
+- bpo-22869: Move the interpreter startup & shutdown code to a new dedicated
+  pylifecycle.c module
 
-- Issue #22847: Improve method cache efficiency.
+- bpo-22847: Improve method cache efficiency.
 
-- Issue #22335: Fix crash when trying to enlarge a bytearray to 0x7fffffff
+- bpo-22335: Fix crash when trying to enlarge a bytearray to 0x7fffffff
   bytes on a 32-bit platform.
 
-- Issue #22653: Fix an assertion failure in debug mode when doing a reentrant
+- bpo-22653: Fix an assertion failure in debug mode when doing a reentrant
   dict insertion in debug mode.
 
-- Issue #22643: Fix integer overflow in Unicode case operations (upper, lower,
+- bpo-22643: Fix integer overflow in Unicode case operations (upper, lower,
   title, swapcase, casefold).
 
-- Issue #17636: Circular imports involving relative imports are now
-  supported.
+- bpo-17636: Circular imports involving relative imports are now supported.
 
-- Issue #22604: Fix assertion error in debug mode when dividing a complex
+- bpo-22604: Fix assertion error in debug mode when dividing a complex
   number by (nan+0j).
 
-- Issue #21052: Do not raise ImportWarning when sys.path_hooks or sys.meta_path
+- bpo-21052: Do not raise ImportWarning when sys.path_hooks or sys.meta_path
   are set to None.
 
-- Issue #16518: Use 'bytes-like object required' in error messages that
+- bpo-16518: Use 'bytes-like object required' in error messages that
   previously used the far more cryptic "'x' does not support the buffer
   protocol.
 
-- Issue #22470: Fixed integer overflow issues in "backslashreplace",
+- bpo-22470: Fixed integer overflow issues in "backslashreplace",
   "xmlcharrefreplace", and "surrogatepass" error handlers.
 
-- Issue #22540: speed up `PyObject_IsInstance` and `PyObject_IsSubclass` in the
+- bpo-22540: speed up `PyObject_IsInstance` and `PyObject_IsSubclass` in the
   common case that the second argument has metaclass `type`.
 
-- Issue #18711: Add a new `PyErr_FormatV` function, similar to `PyErr_Format`
+- bpo-18711: Add a new `PyErr_FormatV` function, similar to `PyErr_Format`
   but accepting a `va_list` argument.
 
-- Issue #22520: Fix overflow checking when generating the repr of a unicode
+- bpo-22520: Fix overflow checking when generating the repr of a unicode
   object.
 
-- Issue #22519: Fix overflow checking in PyBytes_Repr.
+- bpo-22519: Fix overflow checking in PyBytes_Repr.
 
-- Issue #22518: Fix integer overflow issues in latin-1 encoding.
+- bpo-22518: Fix integer overflow issues in latin-1 encoding.
 
-- Issue #16324: _charset parameter of MIMEText now also accepts
+- bpo-16324: _charset parameter of MIMEText now also accepts
   email.charset.Charset instances. Initial patch by Claude Paroz.
 
-- Issue #1764286: Fix inspect.getsource() to support decorated functions.
-  Patch by Claudiu Popa.
+- bpo-1764286: Fix inspect.getsource() to support decorated functions. Patch
+  by Claudiu Popa.
 
-- Issue #18554: os.__all__ includes posix functions.
+- bpo-18554: os.__all__ includes posix functions.
 
-- Issue #21391: Use os.path.abspath in the shutil module.
+- bpo-21391: Use os.path.abspath in the shutil module.
 
-- Issue #11471: avoid generating a JUMP_FORWARD instruction at the end of
-  an if-block if there is no else-clause.  Original patch by Eugene Toder.
+- bpo-11471: avoid generating a JUMP_FORWARD instruction at the end of an
+  if-block if there is no else-clause.  Original patch by Eugene Toder.
 
-- Issue #22215: Now ValueError is raised instead of TypeError when str or bytes
+- bpo-22215: Now ValueError is raised instead of TypeError when str or bytes
   argument contains not permitted null character or byte.
 
-- Issue #22258: Fix the internal function set_inheritable() on Illumos.
-  This platform exposes the function ``ioctl(FIOCLEX)``, but calling it fails
+- bpo-22258: Fix the internal function set_inheritable() on Illumos. This
+  platform exposes the function ``ioctl(FIOCLEX)``, but calling it fails
   with errno is ENOTTY: "Inappropriate ioctl for device". set_inheritable()
-  now falls back to the slower ``fcntl()`` (``F_GETFD`` and then ``F_SETFD``).
+  now falls back to the slower ``fcntl()`` (``F_GETFD`` and then
+  ``F_SETFD``).
 
-- Issue #21389: Displaying the __qualname__ of the underlying function in the
+- bpo-21389: Displaying the __qualname__ of the underlying function in the
   repr of a bound method.
 
-- Issue #22206: Using pthread, PyThread_create_key() now sets errno to ENOMEM
+- bpo-22206: Using pthread, PyThread_create_key() now sets errno to ENOMEM
   and returns -1 (error) on integer overflow.
 
-- Issue #20184: Argument Clinic based signature introspection added for
-  30 of the builtin functions.
+- bpo-20184: Argument Clinic based signature introspection added for 30 of
+  the builtin functions.
 
-- Issue #22116: C functions and methods (of the 'builtin_function_or_method'
+- bpo-22116: C functions and methods (of the 'builtin_function_or_method'
   type) can now be weakref'ed.  Patch by Wei Wu.
 
-- Issue #22077: Improve index error messages for bytearrays, bytes, lists,
-  and tuples by adding 'or slices'. Added ', not <typename>' for bytearrays.
+- bpo-22077: Improve index error messages for bytearrays, bytes, lists, and
+  tuples by adding 'or slices'. Added ', not <typename>' for bytearrays.
   Original patch by Claudiu Popa.
 
-- Issue #20179: Apply Argument Clinic to bytes and bytearray.
-  Patch by Tal Einat.
+- bpo-20179: Apply Argument Clinic to bytes and bytearray. Patch by Tal
+  Einat.
 
-- Issue #22082: Clear interned strings in slotdefs.
+- bpo-22082: Clear interned strings in slotdefs.
 
 - Upgrade Unicode database to Unicode 7.0.0.
 
-- Issue #21897: Fix a crash with the f_locals attribute with closure
-  variables when frame.clear() has been called.
+- bpo-21897: Fix a crash with the f_locals attribute with closure variables
+  when frame.clear() has been called.
 
-- Issue #21205: Add a new ``__qualname__`` attribute to generator, the
+- bpo-21205: Add a new ``__qualname__`` attribute to generator, the
   qualified name, and use it in the representation of a generator
-  (``repr(gen)``). The default name of the generator (``__name__`` attribute)
-  is now get from the function instead of the code. Use ``gen.gi_code.co_name``
-  to get the name of the code.
+  (``repr(gen)``). The default name of the generator (``__name__``
+  attribute) is now get from the function instead of the code. Use
+  ``gen.gi_code.co_name`` to get the name of the code.
 
-- Issue #21669: With the aid of heuristics in SyntaxError.__init__, the
-  parser now attempts to generate more meaningful (or at least more search
-  engine friendly) error messages when "exec" and "print" are used as
-  statements.
+- bpo-21669: With the aid of heuristics in SyntaxError.__init__, the parser
+  now attempts to generate more meaningful (or at least more search engine
+  friendly) error messages when "exec" and "print" are used as statements.
 
-- Issue #21642: In the conditional if-else expression, allow an integer written
-  with no space between itself and the ``else`` keyword (e.g. ``True if 42else
-  False``) to be valid syntax.
+- bpo-21642: In the conditional if-else expression, allow an integer written
+  with no space between itself and the ``else`` keyword (e.g. ``True if
+  42else False``) to be valid syntax.
 
-- Issue #21523: Fix over-pessimistic computation of the stack effect of
-  some opcodes in the compiler.  This also fixes a quadratic compilation
-  time issue noticeable when compiling code with a large number of "and"
-  and "or" operators.
+- bpo-21523: Fix over-pessimistic computation of the stack effect of some
+  opcodes in the compiler.  This also fixes a quadratic compilation time
+  issue noticeable when compiling code with a large number of "and" and "or"
+  operators.
 
-- Issue #21418: Fix a crash in the builtin function super() when called without
+- bpo-21418: Fix a crash in the builtin function super() when called without
   argument and without current frame (ex: embedded Python).
 
-- Issue #21425: Fix flushing of standard streams in the interactive
+- bpo-21425: Fix flushing of standard streams in the interactive
   interpreter.
 
-- Issue #21435: In rare cases, when running finalizers on objects in cyclic
+- bpo-21435: In rare cases, when running finalizers on objects in cyclic
   trash a bad pointer dereference could occur due to a subtle flaw in
   internal iteration logic.
 
-- Issue #21377: PyBytes_Concat() now tries to concatenate in-place when the
+- bpo-21377: PyBytes_Concat() now tries to concatenate in-place when the
   first argument has a reference count of 1.  Patch by Nikolaus Rath.
 
-- Issue #20355: -W command line options now have higher priority than the
+- bpo-20355: -W command line options now have higher priority than the
   PYTHONWARNINGS environment variable.  Patch by Arfrever.
 
-- Issue #21274: Define PATH_MAX for GNU/Hurd in Python/pythonrun.c.
+- bpo-21274: Define PATH_MAX for GNU/Hurd in Python/pythonrun.c.
 
-- Issue #20904: Support setting FPU precision on m68k.
+- bpo-20904: Support setting FPU precision on m68k.
 
-- Issue #21209: Fix sending tuples to custom generator objects with the yield
+- bpo-21209: Fix sending tuples to custom generator objects with the yield
   from syntax.
 
-- Issue #21193: pow(a, b, c) now raises ValueError rather than TypeError when b
-  is negative.  Patch by Josh Rosenberg.
+- bpo-21193: pow(a, b, c) now raises ValueError rather than TypeError when b
+  is negative. Patch by Josh Rosenberg.
 
-- PEP 465 and Issue #21176: Add the '@' operator for matrix multiplication.
+- bpo-21176: PEP 465: Add the '@' operator for matrix multiplication.
 
-- Issue #21134: Fix segfault when str is called on an uninitialized
+- bpo-21134: Fix segfault when str is called on an uninitialized
   UnicodeEncodeError, UnicodeDecodeError, or UnicodeTranslateError object.
 
-- Issue #19537: Fix PyUnicode_DATA() alignment under m68k.  Patch by
-  Andreas Schwab.
+- bpo-19537: Fix PyUnicode_DATA() alignment under m68k.  Patch by Andreas
+  Schwab.
 
-- Issue #20929: Add a type cast to avoid shifting a negative number.
+- bpo-20929: Add a type cast to avoid shifting a negative number.
 
-- Issue #20731: Properly position in source code files even if they
-  are opened in text mode. Patch by Serhiy Storchaka.
+- bpo-20731: Properly position in source code files even if they are opened
+  in text mode. Patch by Serhiy Storchaka.
 
-- Issue #20637: Key-sharing now also works for instance dictionaries of
+- bpo-20637: Key-sharing now also works for instance dictionaries of
   subclasses.  Patch by Peter Ingebretson.
 
-- Issue #8297: Attributes missing from modules now include the module name
-  in the error text.  Original patch by ysj.ray.
+- bpo-8297: Attributes missing from modules now include the module name in
+  the error text.  Original patch by ysj.ray.
 
-- Issue #19995: %c, %o, %x, and %X now raise TypeError on non-integer input.
+- bpo-19995: %c, %o, %x, and %X now raise TypeError on non-integer input.
 
-- Issue #19655: The ASDL parser - used by the build process to generate code for
-  managing the Python AST in C - was rewritten. The new parser is self contained
-  and does not require to carry long the spark.py parser-generator library;
-  spark.py was removed from the source base.
+- bpo-19655: The ASDL parser - used by the build process to generate code
+  for managing the Python AST in C - was rewritten. The new parser is self
+  contained and does not require to carry long the spark.py parser-generator
+  library; spark.py was removed from the source base.
 
-- Issue #12546: Allow ``\x00`` to be used as a fill character when using str, int,
-  float, and complex __format__ methods.
+- bpo-12546: Allow ``\x00`` to be used as a fill character when using str,
+  int, float, and complex __format__ methods.
 
-- Issue #20480: Add ipaddress.reverse_pointer. Patch by Leon Weber.
+- bpo-20480: Add ipaddress.reverse_pointer. Patch by Leon Weber.
 
-- Issue #13598: Modify string.Formatter to support auto-numbering of
-  replacement fields. It now matches the behavior of str.format() in
-  this regard. Patches by Phil Elson and Ramchandra Apte.
+- bpo-13598: Modify string.Formatter to support auto-numbering of
+  replacement fields. It now matches the behavior of str.format() in this
+  regard. Patches by Phil Elson and Ramchandra Apte.
 
-- Issue #8931: Make alternate formatting ('#') for type 'c' raise an
-  exception. In versions prior to 3.5, '#' with 'c' had no effect. Now
-  specifying it is an error.  Patch by Torsten Landschoff.
+- bpo-8931: Make alternate formatting ('#') for type 'c' raise an exception.
+  In versions prior to 3.5, '#' with 'c' had no effect. Now specifying it is
+  an error. Patch by Torsten Landschoff.
 
-- Issue #23165: Perform overflow checks before allocating memory in the
+- bpo-23165: Perform overflow checks before allocating memory in the
   _Py_char2wchar function.
 
 Library
 -------
 
-- Issue #23399: pyvenv creates relative symlinks where possible.
+- bpo-23399: pyvenv creates relative symlinks where possible.
 
-- Issue #20289: cgi.FieldStorage() now supports the context management
+- bpo-20289: cgi.FieldStorage() now supports the context management
   protocol.
 
-- Issue #13128: Print response headers for CONNECT requests when debuglevel
-  0. Patch by Demian Brecht.
+- bpo-13128: Print response headers for CONNECT requests when debuglevel >
+  0. Patch by Demian Brecht.
 
-- Issue #15381: Optimized io.BytesIO to make less allocations and copyings.
+- bpo-15381: Optimized io.BytesIO to make less allocations and copyings.
 
-- Issue #22818: Splitting on a pattern that could match an empty string now
+- bpo-22818: Splitting on a pattern that could match an empty string now
   raises a warning.  Patterns that can only match empty strings are now
   rejected.
 
-- Issue #23099: Closing io.BytesIO with exported buffer is rejected now to
+- bpo-23099: Closing io.BytesIO with exported buffer is rejected now to
   prevent corrupting exported buffer.
 
-- Issue #23326: Removed __ne__ implementations.  Since fixing default __ne__
+- bpo-23326: Removed __ne__ implementations.  Since fixing default __ne__
   implementation in issue #21408 they are redundant.
 
-- Issue #23363: Fix possible overflow in itertools.permutations.
+- bpo-23363: Fix possible overflow in itertools.permutations.
 
-- Issue #23364: Fix possible overflow in itertools.product.
+- bpo-23364: Fix possible overflow in itertools.product.
 
-- Issue #23366: Fixed possible integer overflow in itertools.combinations.
+- bpo-23366: Fixed possible integer overflow in itertools.combinations.
 
-- Issue #23369: Fixed possible integer overflow in
+- bpo-23369: Fixed possible integer overflow in
   _json.encode_basestring_ascii.
 
-- Issue #23353: Fix the exception handling of generators in
+- bpo-23353: Fix the exception handling of generators in
   PyEval_EvalFrameEx(). At entry, save or swap the exception state even if
-  PyEval_EvalFrameEx() is called with throwflag=0. At exit, the exception state
-  is now always restored or swapped, not only if why is WHY_YIELD or
+  PyEval_EvalFrameEx() is called with throwflag=0. At exit, the exception
+  state is now always restored or swapped, not only if why is WHY_YIELD or
   WHY_RETURN. Patch co-written with Antoine Pitrou.
 
-- Issue #14099: Restored support of writing ZIP files to tellable but
-  non-seekable streams.
+- bpo-14099: Restored support of writing ZIP files to tellable but non-
+  seekable streams.
 
-- Issue #14099: Writing to ZipFile and reading multiple ZipExtFiles is
+- bpo-14099: Writing to ZipFile and reading multiple ZipExtFiles is
   threadsafe now.
 
-- Issue #19361: JSON decoder now raises JSONDecodeError instead of ValueError.
+- bpo-19361: JSON decoder now raises JSONDecodeError instead of ValueError.
 
-- Issue #18518: timeit now rejects statements which can't be compiled outside
-  function or a loop (e.g. "return" or "break").
+- bpo-18518: timeit now rejects statements which can't be compiled outside a
+  function or a loop (e.g. "return" or "break").
 
-- Issue #23094: Fixed readline with frames in Python implementation of pickle.
+- bpo-23094: Fixed readline with frames in Python implementation of pickle.
 
-- Issue #23268: Fixed bugs in the comparison of ipaddress classes.
+- bpo-23268: Fixed bugs in the comparison of ipaddress classes.
 
-- Issue #21408: Removed incorrect implementations of __ne__() which didn't
+- bpo-21408: Removed incorrect implementations of __ne__() which didn't
   returned NotImplemented if __eq__() returned NotImplemented.  The default
   __ne__() now works correctly.
 
-- Issue #19996: :class:`email.feedparser.FeedParser` now handles (malformed)
+- bpo-19996: :class:`email.feedparser.FeedParser` now handles (malformed)
   headers with no key rather than assuming the body has started.
 
-- Issue #20188: Support Application-Layer Protocol Negotiation (ALPN) in the ssl
-  module.
+- bpo-20188: Support Application-Layer Protocol Negotiation (ALPN) in the
+  ssl module.
 
-- Issue #23133: Pickling of ipaddress objects now produces more compact and
+- bpo-23133: Pickling of ipaddress objects now produces more compact and
   portable representation.
 
-- Issue #23248: Update ssl error codes from latest OpenSSL git master.
+- bpo-23248: Update ssl error codes from latest OpenSSL git master.
 
-- Issue #23266: Much faster implementation of ipaddress.collapse_addresses()
+- bpo-23266: Much faster implementation of ipaddress.collapse_addresses()
   when there are many non-consecutive addresses.
 
-- Issue #23098: 64-bit dev_t is now supported in the os module.
+- bpo-23098: 64-bit dev_t is now supported in the os module.
 
-- Issue #21817: When an exception is raised in a task submitted to a
-  ProcessPoolExecutor, the remote traceback is now displayed in the
-  parent process.  Patch by Claudiu Popa.
+- bpo-21817: When an exception is raised in a task submitted to a
+  ProcessPoolExecutor, the remote traceback is now displayed in the parent
+  process.  Patch by Claudiu Popa.
 
-- Issue #15955: Add an option to limit output size when decompressing LZMA
+- bpo-15955: Add an option to limit output size when decompressing LZMA
   data.  Patch by Nikolaus Rath and Martin Panter.
 
-- Issue #23250: In the http.cookies module, capitalize "HttpOnly" and "Secure"
+- bpo-23250: In the http.cookies module, capitalize "HttpOnly" and "Secure"
   as they are written in the standard.
 
-- Issue #23063: In the disutils' check command, fix parsing of reST with code or
-  code-block directives.
+- bpo-23063: In the disutils' check command, fix parsing of reST with code
+  or code-block directives.
 
-- Issue #23209, #23225: selectors.BaseSelector.get_key() now raises a
-  RuntimeError if the selector is closed. And selectors.BaseSelector.close()
-  now clears its internal reference to the selector mapping to break a
-  reference cycle. Initial patch written by Martin Richard.
+- bpo-23209: selectors.BaseSelector.get_key() now raises a RuntimeError if
+  the selector is closed. And selectors.BaseSelector.close() now clears its
+  internal reference to the selector mapping to break a reference cycle.
+  Initial patch written by Martin Richard. (See also: bpo-23225)
 
-- Issue #17911: Provide a way to seed the linecache for a PEP-302 module
+- bpo-17911: Provide a way to seed the linecache for a PEP-302 module
   without actually loading the code.
 
-- Issue #17911: Provide a new object API for traceback, including the ability
-  to not lookup lines at all until the traceback is actually rendered, without
-  any trace of the original objects being kept alive.
+- bpo-17911: Provide a new object API for traceback, including the ability
+  to not lookup lines at all until the traceback is actually rendered,
+  without any trace of the original objects being kept alive.
 
-- Issue #19777: Provide a home() classmethod on Path objects.  Contributed
-  by Victor Salgado and Mayank Tripathi.
+- bpo-19777: Provide a home() classmethod on Path objects.  Contributed by
+  Victor Salgado and Mayank Tripathi.
 
-- Issue #23206: Make ``json.dumps(..., ensure_ascii=False)`` as fast as the
+- bpo-23206: Make ``json.dumps(..., ensure_ascii=False)`` as fast as the
   default case of ``ensure_ascii=True``.  Patch by Naoki Inada.
 
-- Issue #23185: Add math.inf and math.nan constants.
+- bpo-23185: Add math.inf and math.nan constants.
 
-- Issue #23186: Add ssl.SSLObject.shared_ciphers() and
+- bpo-23186: Add ssl.SSLObject.shared_ciphers() and
   ssl.SSLSocket.shared_ciphers() to fetch the client's list ciphers sent at
   handshake.
 
-- Issue #23143: Remove compatibility with OpenSSLs older than 0.9.8.
+- bpo-23143: Remove compatibility with OpenSSLs older than 0.9.8.
 
-- Issue #23132: Improve performance and introspection support of comparison
+- bpo-23132: Improve performance and introspection support of comparison
   methods created by functool.total_ordering.
 
-- Issue #19776: Add an expanduser() method on Path objects.
+- bpo-19776: Add an expanduser() method on Path objects.
 
-- Issue #23112: Fix SimpleHTTPServer to correctly carry the query string and
+- bpo-23112: Fix SimpleHTTPServer to correctly carry the query string and
   fragment when it redirects to add a trailing slash.
 
-- Issue #21793: Added http.HTTPStatus enums (i.e. HTTPStatus.OK,
-  HTTPStatus.NOT_FOUND).  Patch by Demian Brecht.
+- bpo-21793: Added http.HTTPStatus enums (i.e. HTTPStatus.OK,
+  HTTPStatus.NOT_FOUND). Patch by Demian Brecht.
 
-- Issue #23093: In the io, module allow more operations to work on detached
+- bpo-23093: In the io, module allow more operations to work on detached
   streams.
 
-- Issue #23111: In the ftplib, make ssl.PROTOCOL_SSLv23 the default protocol
+- bpo-23111: In the ftplib, make ssl.PROTOCOL_SSLv23 the default protocol
   version.
 
-- Issue #22585: On OpenBSD 5.6 and newer, os.urandom() now calls getentropy(),
+- bpo-22585: On OpenBSD 5.6 and newer, os.urandom() now calls getentropy(),
   instead of reading /dev/urandom, to get pseudo-random bytes.
 
-- Issue #19104: pprint now produces evaluable output for wrapped strings.
+- bpo-19104: pprint now produces evaluable output for wrapped strings.
 
-- Issue #23071: Added missing names to codecs.__all__.  Patch by Martin Panter.
+- bpo-23071: Added missing names to codecs.__all__.  Patch by Martin Panter.
 
-- Issue #22783: Pickling now uses the NEWOBJ opcode instead of the NEWOBJ_EX
+- bpo-22783: Pickling now uses the NEWOBJ opcode instead of the NEWOBJ_EX
   opcode if possible.
 
-- Issue #15513: Added a __sizeof__ implementation for pickle classes.
+- bpo-15513: Added a __sizeof__ implementation for pickle classes.
 
-- Issue #19858: pickletools.optimize() now aware of the MEMOIZE opcode, can
+- bpo-19858: pickletools.optimize() now aware of the MEMOIZE opcode, can
   produce more compact result and no longer produces invalid output if input
   data contains MEMOIZE opcodes together with PUT or BINPUT opcodes.
 
-- Issue #22095: Fixed HTTPConnection.set_tunnel with default port.  The port
+- bpo-22095: Fixed HTTPConnection.set_tunnel with default port.  The port
   value in the host header was set to "None".  Patch by Demian Brecht.
 
-- Issue #23016: A warning no longer produces an AttributeError when the program
+- bpo-23016: A warning no longer produces an AttributeError when the program
   is run with pythonw.exe.
 
-- Issue #21775: shutil.copytree(): fix crash when copying to VFAT. An exception
+- bpo-21775: shutil.copytree(): fix crash when copying to VFAT. An exception
   handler assumed that OSError objects always have a 'winerror' attribute.
-  That is not the case, so the exception handler itself raised AttributeError
-  when run on Linux (and, presumably, any other non-Windows OS).
-  Patch by Greg Ward.
+  That is not the case, so the exception handler itself raised
+  AttributeError when run on Linux (and, presumably, any other non-Windows
+  OS). Patch by Greg Ward.
 
-- Issue #1218234: Fix inspect.getsource() to load updated source of
-  reloaded module. Initial patch by Berker Peksag.
+- bpo-1218234: Fix inspect.getsource() to load updated source of reloaded
+  module. Initial patch by Berker Peksag.
 
-- Issue #21740: Support wrapped callables in doctest. Patch by Claudiu Popa.
+- bpo-21740: Support wrapped callables in doctest. Patch by Claudiu Popa.
 
-- Issue #23009: Make sure selectors.EpollSelecrtor.select() works when no
-  FD is registered.
+- bpo-23009: Make sure selectors.EpollSelecrtor.select() works when no FD is
+  registered.
 
-- Issue #22959: In the constructor of http.client.HTTPSConnection, prefer the
+- bpo-22959: In the constructor of http.client.HTTPSConnection, prefer the
   context's check_hostname attribute over the *check_hostname* parameter.
 
-- Issue #22696: Add function :func:`sys.is_finalizing` to know about
+- bpo-22696: Add function :func:`sys.is_finalizing` to know about
   interpreter shutdown.
 
-- Issue #16043: Add a default limit for the amount of data xmlrpclib.gzip_decode
-  will return. This resolves CVE-2013-1753.
+- bpo-16043: Add a default limit for the amount of data
+  xmlrpclib.gzip_decode will return. This resolves CVE-2013-1753.
 
-- Issue #14099: ZipFile.open() no longer reopen the underlying file.  Objects
-  returned by ZipFile.open() can now operate independently of the ZipFile even
-  if the ZipFile was created by passing in a file-like object as the first
-  argument to the constructor.
+- bpo-14099: ZipFile.open() no longer reopen the underlying file.  Objects
+  returned by ZipFile.open() can now operate independently of the ZipFile
+  even if the ZipFile was created by passing in a file-like object as the
+  first argument to the constructor.
 
-- Issue #22966: Fix __pycache__ pyc file name clobber when pyc_compile is
-  asked to compile a source file containing multiple dots in the source file
-  name.
+- bpo-22966: Fix __pycache__ pyc file name clobber when pyc_compile is asked
+  to compile a source file containing multiple dots in the source file name.
 
-- Issue #21971: Update turtledemo doc and add module to the index.
+- bpo-21971: Update turtledemo doc and add module to the index.
 
-- Issue #21032: Fixed socket leak if HTTPConnection.getresponse() fails.
+- bpo-21032: Fixed socket leak if HTTPConnection.getresponse() fails.
   Original patch by Martin Panter.
 
-- Issue #22407: Deprecated the use of re.LOCALE flag with str patterns or
+- bpo-22407: Deprecated the use of re.LOCALE flag with str patterns or
   re.ASCII. It was newer worked.
 
-- Issue #22902: The "ip" command is now used on Linux to determine MAC address
+- bpo-22902: The "ip" command is now used on Linux to determine MAC address
   in uuid.getnode().  Pach by Bruno Cauet.
 
-- Issue #22960: Add a context argument to xmlrpclib.ServerProxy constructor.
+- bpo-22960: Add a context argument to xmlrpclib.ServerProxy constructor.
 
-- Issue #22389: Add contextlib.redirect_stderr().
+- bpo-22389: Add contextlib.redirect_stderr().
 
-- Issue #21356: Make ssl.RAND_egd() optional to support LibreSSL. The
-  availability of the function is checked during the compilation. Patch written
-  by Bernard Spil.
+- bpo-21356: Make ssl.RAND_egd() optional to support LibreSSL. The
+  availability of the function is checked during the compilation. Patch
+  written by Bernard Spil.
 
-- Issue #22915: SAX parser now supports files opened with file descriptor or
+- bpo-22915: SAX parser now supports files opened with file descriptor or
   bytes path.
 
-- Issue #22609: Constructors and update methods of mapping classes in the
+- bpo-22609: Constructors and update methods of mapping classes in the
   collections module now accept the self keyword argument.
 
-- Issue #22940: Add readline.append_history_file.
+- bpo-22940: Add readline.append_history_file.
 
-- Issue #19676: Added the "namereplace" error handler.
+- bpo-19676: Added the "namereplace" error handler.
 
-- Issue #22788: Add *context* parameter to logging.handlers.HTTPHandler.
+- bpo-22788: Add *context* parameter to logging.handlers.HTTPHandler.
 
-- Issue #22921: Allow SSLContext to take the *hostname* parameter even if
+- bpo-22921: Allow SSLContext to take the *hostname* parameter even if
   OpenSSL doesn't support SNI.
 
-- Issue #22894: TestCase.subTest() would cause the test suite to be stopped
+- bpo-22894: TestCase.subTest() would cause the test suite to be stopped
   when in failfast mode, even in the absence of failures.
 
-- Issue #22796: HTTP cookie parsing is now stricter, in order to protect
+- bpo-22796: HTTP cookie parsing is now stricter, in order to protect
   against potential injection attacks.
 
-- Issue #22370: Windows detection in pathlib is now more robust.
+- bpo-22370: Windows detection in pathlib is now more robust.
 
-- Issue #22841: Reject coroutines in asyncio add_signal_handler().
-  Patch by Ludovic.Gasc.
+- bpo-22841: Reject coroutines in asyncio add_signal_handler(). Patch by
+  Ludovic.Gasc.
 
-- Issue #19494: Added urllib.request.HTTPBasicPriorAuthHandler. Patch by
-  Matej Cepl.
+- bpo-19494: Added urllib.request.HTTPBasicPriorAuthHandler. Patch by Matej
+  Cepl.
 
-- Issue #22578: Added attributes to the re.error class.
+- bpo-22578: Added attributes to the re.error class.
 
-- Issue #22849: Fix possible double free in the io.TextIOWrapper constructor.
+- bpo-22849: Fix possible double free in the io.TextIOWrapper constructor.
 
-- Issue #12728: Different Unicode characters having the same uppercase but
-  different lowercase are now matched in case-insensitive regular expressions.
+- bpo-12728: Different Unicode characters having the same uppercase but
+  different lowercase are now matched in case-insensitive regular
+  expressions.
 
-- Issue #22821: Fixed fcntl() with integer argument on 64-bit big-endian
+- bpo-22821: Fixed fcntl() with integer argument on 64-bit big-endian
   platforms.
 
-- Issue #21650: Add an `--sort-keys` option to json.tool CLI.
+- bpo-21650: Add an `--sort-keys` option to json.tool CLI.
 
-- Issue #22824: Updated reprlib output format for sets to use set literals.
+- bpo-22824: Updated reprlib output format for sets to use set literals.
   Patch contributed by Berker Peksag.
 
-- Issue #22824: Updated reprlib output format for arrays to display empty
+- bpo-22824: Updated reprlib output format for arrays to display empty
   arrays without an unnecessary empty list.  Suggested by Serhiy Storchaka.
 
-- Issue #22406: Fixed the uu_codec codec incorrectly ported to 3.x.
-  Based on patch by Martin Panter.
+- bpo-22406: Fixed the uu_codec codec incorrectly ported to 3.x. Based on
+  patch by Martin Panter.
 
-- Issue #17293: uuid.getnode() now determines MAC address on AIX using netstat.
+- bpo-17293: uuid.getnode() now determines MAC address on AIX using netstat.
   Based on patch by Aivars Kalvāns.
 
-- Issue #22769: Fixed ttk.Treeview.tag_has() when called without arguments.
+- bpo-22769: Fixed ttk.Treeview.tag_has() when called without arguments.
 
-- Issue #22417: Verify certificates by default in httplib (PEP 476).
+- bpo-22417: Verify certificates by default in httplib (PEP 476).
 
-- Issue #22775: Fixed unpickling of http.cookies.SimpleCookie with protocol 2
-  and above.  Patch by Tim Graham.
+- bpo-22775: Fixed unpickling of http.cookies.SimpleCookie with protocol 2
+  and above. Patch by Tim Graham.
 
-- Issue #22776: Brought excluded code into the scope of a try block in
+- bpo-22776: Brought excluded code into the scope of a try block in
   SysLogHandler.emit().
 
-- Issue #22665: Add missing get_terminal_size and SameFileError to
+- bpo-22665: Add missing get_terminal_size and SameFileError to
   shutil.__all__.
 
-- Issue #6623: Remove deprecated Netrc class in the ftplib module. Patch by
+- bpo-6623: Remove deprecated Netrc class in the ftplib module. Patch by
   Matt Chaput.
 
-- Issue #17381: Fixed handling of case-insensitive ranges in regular
+- bpo-17381: Fixed handling of case-insensitive ranges in regular
   expressions.
 
-- Issue #22410: Module level functions in the re module now cache compiled
+- bpo-22410: Module level functions in the re module now cache compiled
   locale-dependent regular expressions taking into account the locale.
 
-- Issue #22759: Query methods on pathlib.Path() (exists(), is_dir(), etc.)
-  now return False when the underlying stat call raises NotADirectoryError.
+- bpo-22759: Query methods on pathlib.Path() (exists(), is_dir(), etc.) now
+  return False when the underlying stat call raises NotADirectoryError.
 
-- Issue #8876: distutils now falls back to copying files when hard linking
-  doesn't work.  This allows use with special filesystems such as VirtualBox
+- bpo-8876: distutils now falls back to copying files when hard linking
+  doesn't work. This allows use with special filesystems such as VirtualBox
   shared folders.
 
-- Issue #22217: Implemented reprs of classes in the zipfile module.
+- bpo-22217: Implemented reprs of classes in the zipfile module.
 
-- Issue #22457: Honour load_tests in the start_dir of discovery.
+- bpo-22457: Honour load_tests in the start_dir of discovery.
 
-- Issue #18216: gettext now raises an error when a .mo file has an
-  unsupported major version number.  Patch by Aaron Hill.
+- bpo-18216: gettext now raises an error when a .mo file has an unsupported
+  major version number.  Patch by Aaron Hill.
 
-- Issue #13918: Provide a locale.delocalize() function which can remove
-  locale-specific number formatting from a string representing a number,
-  without then converting it to a specific type.  Patch by Cédric Krier.
+- bpo-13918: Provide a locale.delocalize() function which can remove locale-
+  specific number formatting from a string representing a number, without
+  then converting it to a specific type.  Patch by Cédric Krier.
 
-- Issue #22676: Make the pickling of global objects which don't have a
+- bpo-22676: Make the pickling of global objects which don't have a
   __module__ attribute less slow.
 
-- Issue #18853: Fixed ResourceWarning in shlex.__nain__.
+- bpo-18853: Fixed ResourceWarning in shlex.__nain__.
 
-- Issue #9351: Defaults set with set_defaults on an argparse subparser
-  are no longer ignored when also set on the parent parser.
+- bpo-9351: Defaults set with set_defaults on an argparse subparser are no
+  longer ignored when also set on the parent parser.
 
-- Issue #7559: unittest test loading ImportErrors are reported as import errors
-  with their import exception rather than as attribute errors after the import
-  has already failed.
+- bpo-7559: unittest test loading ImportErrors are reported as import errors
+  with their import exception rather than as attribute errors after the
+  import has already failed.
 
-- Issue #19746: Make it possible to examine the errors from unittest
-  discovery without executing the test suite. The new `errors` attribute
-  on TestLoader exposes these non-fatal errors encountered during discovery.
+- bpo-19746: Make it possible to examine the errors from unittest discovery
+  without executing the test suite. The new `errors` attribute on TestLoader
+  exposes these non-fatal errors encountered during discovery.
 
-- Issue #21991: Make email.headerregistry's header 'params' attributes
-  be read-only (MappingProxyType).  Previously the dictionary was modifiable
-  but a new one was created on each access of the attribute.
+- bpo-21991: Make email.headerregistry's header 'params' attributes be read-
+  only (MappingProxyType).  Previously the dictionary was modifiable but a
+  new one was created on each access of the attribute.
 
-- Issue #22638: SSLv3 is now disabled throughout the standard library.
-  It can still be enabled by instantiating a SSLContext manually.
+- bpo-22638: SSLv3 is now disabled throughout the standard library. It can
+  still be enabled by instantiating a SSLContext manually.
 
-- Issue #22641: In asyncio, the default SSL context for client connections
-  is now created using ssl.create_default_context(), for stronger security.
+- bpo-22641: In asyncio, the default SSL context for client connections is
+  now created using ssl.create_default_context(), for stronger security.
 
-- Issue #17401: Include closefd in io.FileIO repr.
+- bpo-17401: Include closefd in io.FileIO repr.
 
-- Issue #21338: Add silent mode for compileall. quiet parameters of
-  compile_{dir, file, path} functions now have a multilevel value. Also,
-  -q option of the CLI now have a multilevel value. Patch by Thomas Kluyver.
+- bpo-21338: Add silent mode for compileall. quiet parameters of
+  compile_{dir, file, path} functions now have a multilevel value. Also, -q
+  option of the CLI now have a multilevel value. Patch by Thomas Kluyver.
 
-- Issue #20152: Convert the array and cmath modules to Argument Clinic.
+- bpo-20152: Convert the array and cmath modules to Argument Clinic.
 
-- Issue #18643: Add socket.socketpair() on Windows.
+- bpo-18643: Add socket.socketpair() on Windows.
 
-- Issue #22435: Fix a file descriptor leak when socketserver bind fails.
+- bpo-22435: Fix a file descriptor leak when socketserver bind fails.
 
-- Issue #13096: Fixed segfault in CTypes POINTER handling of large
-  values.
+- bpo-13096: Fixed segfault in CTypes POINTER handling of large values.
 
-- Issue #11694: Raise ConversionError in xdrlib as documented.  Patch
-  by Filip Gruszczyński and Claudiu Popa.
+- bpo-11694: Raise ConversionError in xdrlib as documented.  Patch by Filip
+  Gruszczyński and Claudiu Popa.
 
-- Issue #19380: Optimized parsing of regular expressions.
+- bpo-19380: Optimized parsing of regular expressions.
 
-- Issue #1519638: Now unmatched groups are replaced with empty strings in re.sub()
-  and re.subn().
+- bpo-1519638: Now unmatched groups are replaced with empty strings in
+  re.sub() and re.subn().
 
-- Issue #18615: sndhdr.what/whathdr now return a namedtuple.
+- bpo-18615: sndhdr.what/whathdr now return a namedtuple.
 
-- Issue #22462: Fix pyexpat's creation of a dummy frame to make it
-  appear in exception tracebacks.
+- bpo-22462: Fix pyexpat's creation of a dummy frame to make it appear in
+  exception tracebacks.
 
-- Issue #21965: Add support for in-memory SSL to the ssl module.  Patch
-  by Geert Jansen.
+- bpo-21965: Add support for in-memory SSL to the ssl module.  Patch by
+  Geert Jansen.
 
-- Issue #21173: Fix len() on a WeakKeyDictionary when .clear() was called
-  with an iterator alive.
+- bpo-21173: Fix len() on a WeakKeyDictionary when .clear() was called with
+  an iterator alive.
 
-- Issue #11866: Eliminated race condition in the computation of names
-  for new threads.
+- bpo-11866: Eliminated race condition in the computation of names for new
+  threads.
 
-- Issue #21905: Avoid RuntimeError in pickle.whichmodule() when sys.modules
-  is mutated while iterating.  Patch by Olivier Grisel.
+- bpo-21905: Avoid RuntimeError in pickle.whichmodule() when sys.modules is
+  mutated while iterating.  Patch by Olivier Grisel.
 
-- Issue #11271: concurrent.futures.Executor.map() now takes a *chunksize*
+- bpo-11271: concurrent.futures.Executor.map() now takes a *chunksize*
   argument to allow batching of tasks in child processes and improve
   performance of ProcessPoolExecutor.  Patch by Dan O'Reilly.
 
-- Issue #21883: os.path.join() and os.path.relpath() now raise a TypeError with
-  more helpful error message for unsupported or mismatched types of arguments.
+- bpo-21883: os.path.join() and os.path.relpath() now raise a TypeError with
+  more helpful error message for unsupported or mismatched types of
+  arguments.
 
-- Issue #22219: The zipfile module CLI now adds entries for directories
+- bpo-22219: The zipfile module CLI now adds entries for directories
   (including empty directories) in ZIP file.
 
-- Issue #22449: In the ssl.SSLContext.load_default_certs, consult the
+- bpo-22449: In the ssl.SSLContext.load_default_certs, consult the
   environmental variables SSL_CERT_DIR and SSL_CERT_FILE on Windows.
 
-- Issue #22508: The email.__version__ variable has been removed; the email
-  code is no longer shipped separately from the stdlib, and __version__
-  hasn't been updated in several releases.
+- bpo-22508: The email.__version__ variable has been removed; the email code
+  is no longer shipped separately from the stdlib, and __version__ hasn't
+  been updated in several releases.
 
-- Issue #20076: Added non derived UTF-8 aliases to locale aliases table.
+- bpo-20076: Added non derived UTF-8 aliases to locale aliases table.
 
-- Issue #20079: Added locales supported in glibc 2.18 to locale alias table.
+- bpo-20079: Added locales supported in glibc 2.18 to locale alias table.
 
-- Issue #20218: Added convenience methods read_text/write_text and read_bytes/
+- bpo-20218: Added convenience methods read_text/write_text and read_bytes/
   write_bytes to pathlib.Path objects.
 
-- Issue #22396: On 32-bit AIX platform, don't expose os.posix_fadvise() nor
+- bpo-22396: On 32-bit AIX platform, don't expose os.posix_fadvise() nor
   os.posix_fallocate() because their prototypes in system headers are wrong.
 
-- Issue #22517: When an io.BufferedRWPair object is deallocated, clear its
+- bpo-22517: When an io.BufferedRWPair object is deallocated, clear its
   weakrefs.
 
-- Issue #22437: Number of capturing groups in regular expression is no longer
+- bpo-22437: Number of capturing groups in regular expression is no longer
   limited by 100.
 
-- Issue #17442: InteractiveInterpreter now displays the full chained traceback
-  in its showtraceback method, to match the built in interactive interpreter.
-
-- Issue #23392: Added tests for marshal C API that works with FILE*.
+- bpo-17442: InteractiveInterpreter now displays the full chained traceback
+  in its showtraceback method, to match the built in interactive
+  interpreter.
 
+- bpo-23392: Added tests for marshal C API that works with FILE*.
 
-- Issue #10510: distutils register and upload methods now use HTML standards
+- bpo-10510: distutils register and upload methods now use HTML standards
   compliant CRLF line endings.
 
-- Issue #9850: Fixed macpath.join() for empty first component.  Patch by
-  Oleg Oshmyan.
+- bpo-9850: Fixed macpath.join() for empty first component.  Patch by Oleg
+  Oshmyan.
 
-- Issue #5309: distutils' build and build_ext commands now accept a ``-j``
+- bpo-5309: distutils' build and build_ext commands now accept a ``-j``
   option to enable parallel building of extension modules.
 
-- Issue #22448: Improve canceled timer handles cleanup to prevent
-  unbound memory usage. Patch by Joshua Moore-Oliva.
+- bpo-22448: Improve canceled timer handles cleanup to prevent unbound
+  memory usage. Patch by Joshua Moore-Oliva.
 
-- Issue #22427: TemporaryDirectory no longer attempts to clean up twice when
+- bpo-22427: TemporaryDirectory no longer attempts to clean up twice when
   used in the with statement in generator.
 
-- Issue #22362: Forbidden ambiguous octal escapes out of range 0-0o377 in
+- bpo-22362: Forbidden ambiguous octal escapes out of range 0-0o377 in
   regular expressions.
 
-- Issue #20912: Now directories added to ZIP file have correct Unix and MS-DOS
+- bpo-20912: Now directories added to ZIP file have correct Unix and MS-DOS
   directory attributes.
 
-- Issue #21866: ZipFile.close() no longer writes ZIP64 central directory
+- bpo-21866: ZipFile.close() no longer writes ZIP64 central directory
   records if allowZip64 is false.
 
-- Issue #22278: Fix urljoin problem with relative urls, a regression observed
+- bpo-22278: Fix urljoin problem with relative urls, a regression observed
   after changes to issue22118 were submitted.
 
-- Issue #22415: Fixed debugging output of the GROUPREF_EXISTS opcode in the re
-  module.  Removed trailing spaces in debugging output.
+- bpo-22415: Fixed debugging output of the GROUPREF_EXISTS opcode in the re
+  module. Removed trailing spaces in debugging output.
 
-- Issue #22423: Unhandled exception in thread no longer causes unhandled
+- bpo-22423: Unhandled exception in thread no longer causes unhandled
   AttributeError when sys.stderr is None.
 
-- Issue #21332: Ensure that ``bufsize=1`` in subprocess.Popen() selects
-  line buffering, rather than block buffering.  Patch by Akira Li.
+- bpo-21332: Ensure that ``bufsize=1`` in subprocess.Popen() selects line
+  buffering, rather than block buffering.  Patch by Akira Li.
 
-- Issue #21091: Fix API bug: email.message.EmailMessage.is_attachment is now
-  method.
+- bpo-21091: Fix API bug: email.message.EmailMessage.is_attachment is now a
+  method.
 
-- Issue #21079: Fix email.message.EmailMessage.is_attachment to return the
+- bpo-21079: Fix email.message.EmailMessage.is_attachment to return the
   correct result when the header has parameters as well as a value.
 
-- Issue #22247: Add NNTPError to nntplib.__all__.
+- bpo-22247: Add NNTPError to nntplib.__all__.
 
-- Issue #22366: urllib.request.urlopen will accept a context object
+- bpo-22366: urllib.request.urlopen will accept a context object
   (SSLContext) as an argument which will then be used for HTTPS connection.
   Patch by Alex Gaynor.
 
-- Issue #4180: The warnings registries are now reset when the filters
-  are modified.
+- bpo-4180: The warnings registries are now reset when the filters are
+  modified.
 
-- Issue #22419: Limit the length of incoming HTTP request in wsgiref server to
-  65536 bytes and send a 414 error code for higher lengths. Patch contributed
-  by Devin Cook.
+- bpo-22419: Limit the length of incoming HTTP request in wsgiref server to
+  65536 bytes and send a 414 error code for higher lengths. Patch
+  contributed by Devin Cook.
 
 - Lax cookie parsing in http.cookies could be a security issue when combined
   with non-standard cookie handling in some Web browsers.  Reported by
   Sergey Bobrov.
 
-- Issue #20537: logging methods now accept an exception instance as well as a
+- bpo-20537: logging methods now accept an exception instance as well as a
   Boolean value or exception tuple. Thanks to Yury Selivanov for the patch.
 
-- Issue #22384: An exception in Tkinter callback no longer crashes the program
+- bpo-22384: An exception in Tkinter callback no longer crashes the program
   when it is run with pythonw.exe.
 
-- Issue #22168: Prevent turtle AttributeError with non-default Canvas on OS X.
+- bpo-22168: Prevent turtle AttributeError with non-default Canvas on OS X.
 
-- Issue #21147: sqlite3 now raises an exception if the request contains a null
+- bpo-21147: sqlite3 now raises an exception if the request contains a null
   character instead of truncating it.  Based on patch by Victor Stinner.
 
-- Issue #13968: The glob module now supports recursive search in
-  subdirectories using the ``**`` pattern.
+- bpo-13968: The glob module now supports recursive search in subdirectories
+  using the ``**`` pattern.
 
-- Issue #21951: Fixed a crash in Tkinter on AIX when called Tcl command with
+- bpo-21951: Fixed a crash in Tkinter on AIX when called Tcl command with
   empty string or tuple argument.
 
-- Issue #21951: Tkinter now most likely raises MemoryError instead of crash
-  if the memory allocation fails.
+- bpo-21951: Tkinter now most likely raises MemoryError instead of crash if
+  the memory allocation fails.
 
-- Issue #22338: Fix a crash in the json module on memory allocation failure.
+- bpo-22338: Fix a crash in the json module on memory allocation failure.
 
-- Issue #12410: imaplib.IMAP4 now supports the context management protocol.
+- bpo-12410: imaplib.IMAP4 now supports the context management protocol.
   Original patch by Tarek Ziadé.
 
-- Issue #21270: We now override tuple methods in mock.call objects so that
-  they can be used as normal call attributes.
+- bpo-21270: We now override tuple methods in mock.call objects so that they
+  can be used as normal call attributes.
 
-- Issue #16662: load_tests() is now unconditionally run when it is present in
-  package's __init__.py.  TestLoader.loadTestsFromModule() still accepts
+- bpo-16662: load_tests() is now unconditionally run when it is present in a
+  package's __init__.py.  TestLoader.loadTestsFromModule() still accepts
   use_load_tests, but it is deprecated and ignored.  A new keyword-only
-  attribute `pattern` is added and documented.  Patch given by Robert Collins,
-  tweaked by Barry Warsaw.
+  attribute `pattern` is added and documented.  Patch given by Robert
+  Collins, tweaked by Barry Warsaw.
 
-- Issue #22226: First letter no longer is stripped from the "status" key in
-  the result of Treeview.heading().
+- bpo-22226: First letter no longer is stripped from the "status" key in the
+  result of Treeview.heading().
 
-- Issue #19524: Fixed resource leak in the HTTP connection when an invalid
+- bpo-19524: Fixed resource leak in the HTTP connection when an invalid
   response is received.  Patch by Martin Panter.
 
-- Issue #20421: Add a .version() method to SSL sockets exposing the actual
+- bpo-20421: Add a .version() method to SSL sockets exposing the actual
   protocol version in use.
 
-- Issue #19546: configparser exceptions no longer expose implementation details.
-  Chained KeyErrors are removed, which leads to cleaner tracebacks.  Patch by
-  Claudiu Popa.
+- bpo-19546: configparser exceptions no longer expose implementation
+  details. Chained KeyErrors are removed, which leads to cleaner tracebacks.
+  Patch by Claudiu Popa.
 
-- Issue #22051: turtledemo no longer reloads examples to re-run them.
-  Initialization of variables and gui setup should be done in main(),
-  which is called each time a demo is run, but not on import.
+- bpo-22051: turtledemo no longer reloads examples to re-run them.
+  Initialization of variables and gui setup should be done in main(), which
+  is called each time a demo is run, but not on import.
 
-- Issue #21933: Turtledemo users can change the code font size with a menu
-  selection or control(command) '-' or '+' or control-mousewheel.
-  Original patch by Lita Cho.
+- bpo-21933: Turtledemo users can change the code font size with a menu
+  selection or control(command) '-' or '+' or control-mousewheel. Original
+  patch by Lita Cho.
 
-- Issue #21597: The separator between the turtledemo text pane and the drawing
-  canvas can now be grabbed and dragged with a mouse.  The code text pane can
-  be widened to easily view or copy the full width of the text.  The canvas
-  can be widened on small screens.  Original patches by Jan Kanis and Lita Cho.
+- bpo-21597: The separator between the turtledemo text pane and the drawing
+  canvas can now be grabbed and dragged with a mouse.  The code text pane
+  can be widened to easily view or copy the full width of the text.  The
+  canvas can be widened on small screens.  Original patches by Jan Kanis and
+  Lita Cho.
 
-- Issue #18132: Turtledemo buttons no longer disappear when the window is
+- bpo-18132: Turtledemo buttons no longer disappear when the window is
   shrunk.  Original patches by Jan Kanis and Lita Cho.
 
-- Issue #22043: time.monotonic() is now always available.
+- bpo-22043: time.monotonic() is now always available.
   ``threading.Lock.acquire()``, ``threading.RLock.acquire()`` and socket
   operations now use a monotonic clock, instead of the system clock, when a
   timeout is used.
 
-- Issue #21527: Add a default number of workers to ThreadPoolExecutor equal
-  to 5 times the number of CPUs.  Patch by Claudiu Popa.
+- bpo-21527: Add a default number of workers to ThreadPoolExecutor equal to
+  5 times the number of CPUs.  Patch by Claudiu Popa.
 
-- Issue #22216: smtplib now resets its state more completely after a quit.  The
+- bpo-22216: smtplib now resets its state more completely after a quit.  The
   most obvious consequence of the previous behavior was a STARTTLS failure
   during a connect/starttls/quit/connect/starttls sequence.
 
-- Issue #22098: ctypes' BigEndianStructure and LittleEndianStructure now
-  define an empty __slots__ so that subclasses don't always get an instance
-  dict.  Patch by Claudiu Popa.
+- bpo-22098: ctypes' BigEndianStructure and LittleEndianStructure now define
+  an empty __slots__ so that subclasses don't always get an instance dict.
+  Patch by Claudiu Popa.
 
-- Issue #22185: Fix an occasional RuntimeError in threading.Condition.wait()
+- bpo-22185: Fix an occasional RuntimeError in threading.Condition.wait()
   caused by mutation of the waiters queue without holding the lock.  Patch
   by Doug Zongker.
 
-- Issue #22287: On UNIX, _PyTime_gettimeofday() now uses
+- bpo-22287: On UNIX, _PyTime_gettimeofday() now uses
   clock_gettime(CLOCK_REALTIME) if available. As a side effect, Python now
-  depends on the librt library on Solaris and on Linux (only with glibc older
-  than 2.17).
+  depends on the librt library on Solaris and on Linux (only with glibc
+  older than 2.17).
 
-- Issue #22182: Use e.args to unpack exceptions correctly in
+- bpo-22182: Use e.args to unpack exceptions correctly in
   distutils.file_util.move_file. Patch by Claudiu Popa.
 
 - The webbrowser module now uses subprocess's start_new_session=True rather
   than a potentially risky preexec_fn=os.setsid call.
 
-- Issue #22042: signal.set_wakeup_fd(fd) now raises an exception if the file
+- bpo-22042: signal.set_wakeup_fd(fd) now raises an exception if the file
   descriptor is in blocking mode.
 
-- Issue #16808: inspect.stack() now returns a named tuple instead of a tuple.
+- bpo-16808: inspect.stack() now returns a named tuple instead of a tuple.
   Patch by Daniel Shahaf.
 
-- Issue #22236: Fixed Tkinter images copying operations in NoDefaultRoot mode.
+- bpo-22236: Fixed Tkinter images copying operations in NoDefaultRoot mode.
 
-- Issue #2527: Add a *globals* argument to timeit functions, in order to
-  override the globals namespace in which the timed code is executed.
-  Patch by Ben Roberts.
+- bpo-2527: Add a *globals* argument to timeit functions, in order to
+  override the globals namespace in which the timed code is executed. Patch
+  by Ben Roberts.
 
-- Issue #22118: Switch urllib.parse to use RFC 3986 semantics for the
-  resolution of relative URLs, rather than RFCs 1808 and 2396.
-  Patch by Demian Brecht.
+- bpo-22118: Switch urllib.parse to use RFC 3986 semantics for the
+  resolution of relative URLs, rather than RFCs 1808 and 2396. Patch by
+  Demian Brecht.
 
-- Issue #21549: Added the "members" parameter to TarFile.list().
+- bpo-21549: Added the "members" parameter to TarFile.list().
 
-- Issue #19628: Allow compileall recursion depth to be specified with a -r
+- bpo-19628: Allow compileall recursion depth to be specified with a -r
   option.
 
-- Issue #15696: Add a __sizeof__ implementation for mmap objects on Windows.
+- bpo-15696: Add a __sizeof__ implementation for mmap objects on Windows.
 
-- Issue #22068: Avoided reference loops with Variables and Fonts in Tkinter.
+- bpo-22068: Avoided reference loops with Variables and Fonts in Tkinter.
 
-- Issue #22165: SimpleHTTPRequestHandler now supports undecodable file names.
+- bpo-22165: SimpleHTTPRequestHandler now supports undecodable file names.
 
-- Issue #15381: Optimized line reading in io.BytesIO.
+- bpo-15381: Optimized line reading in io.BytesIO.
 
-- Issue #8797: Raise HTTPError on failed Basic Authentication immediately.
+- bpo-8797: Raise HTTPError on failed Basic Authentication immediately.
   Initial patch by Sam Bull.
 
-- Issue #20729: Restored the use of lazy iterkeys()/itervalues()/iteritems()
-  in the mailbox module.
+- bpo-20729: Restored the use of lazy iterkeys()/itervalues()/iteritems() in
+  the mailbox module.
 
-- Issue #21448: Changed FeedParser feed() to avoid O(N**2) behavior when
-  parsing long line.  Original patch by Raymond Hettinger.
+- bpo-21448: Changed FeedParser feed() to avoid O(N**2) behavior when
+  parsing long line. Original patch by Raymond Hettinger.
 
-- Issue #22184: The functools LRU Cache decorator factory now gives an earlier
+- bpo-22184: The functools LRU Cache decorator factory now gives an earlier
   and clearer error message when the user forgets the required parameters.
 
-- Issue #17923: glob() patterns ending with a slash no longer match non-dirs on
+- bpo-17923: glob() patterns ending with a slash no longer match non-dirs on
   AIX.  Based on patch by Delhallt.
 
-- Issue #21725: Added support for RFC 6531 (SMTPUTF8) in smtpd.
+- bpo-21725: Added support for RFC 6531 (SMTPUTF8) in smtpd.
 
-- Issue #22176: Update the ctypes module's libffi to v3.1.  This release
-  adds support for the Linux AArch64 and POWERPC ELF ABIv2 little endian
+- bpo-22176: Update the ctypes module's libffi to v3.1.  This release adds
+  support for the Linux AArch64 and POWERPC ELF ABIv2 little endian
   architectures.
 
-- Issue #5411: Added support for the "xztar" format in the shutil module.
+- bpo-5411: Added support for the "xztar" format in the shutil module.
 
-- Issue #21121: Don't force 3rd party C extensions to be built with
-  -Werror=declaration-after-statement.
+- bpo-21121: Don't force 3rd party C extensions to be built with
+  -Werror=declaration- after-statement.
 
-- Issue #21975: Fixed crash when using uninitialized sqlite3.Row (in particular
-  when unpickling pickled sqlite3.Row).  sqlite3.Row is now initialized in the
-  __new__() method.
+- bpo-21975: Fixed crash when using uninitialized sqlite3.Row (in particular
+  when unpickling pickled sqlite3.Row).  sqlite3.Row is now initialized in
+  the __new__() method.
 
-- Issue #20170: Convert posixmodule to use Argument Clinic.
+- bpo-20170: Convert posixmodule to use Argument Clinic.
 
-- Issue #21539: Add an *exists_ok* argument to `Pathlib.mkdir()` to mimic
+- bpo-21539: Add an *exists_ok* argument to `Pathlib.mkdir()` to mimic
   `mkdir -p` and `os.makedirs()` functionality.  When true, ignore
   FileExistsErrors.  Patch by Berker Peksag.
 
-- Issue #22127: Bypass IDNA for pure-ASCII host names in the socket module
-  (in particular for numeric IPs).
+- bpo-22127: Bypass IDNA for pure-ASCII host names in the socket module (in
+  particular for numeric IPs).
 
-- Issue #21047: set the default value for the *convert_charrefs* argument
-  of HTMLParser to True.  Patch by Berker Peksag.
+- bpo-21047: set the default value for the *convert_charrefs* argument of
+  HTMLParser to True.  Patch by Berker Peksag.
 
 - Add an __all__ to html.entities.
 
-- Issue #15114: the strict mode and argument of HTMLParser, HTMLParser.error,
+- bpo-15114: the strict mode and argument of HTMLParser, HTMLParser.error,
   and the HTMLParserError exception have been removed.
 
-- Issue #22085: Dropped support of Tk 8.3 in Tkinter.
+- bpo-22085: Dropped support of Tk 8.3 in Tkinter.
 
-- Issue #21580: Now Tkinter correctly handles bytes arguments passed to Tk.
-  In particular this allows initializing images from binary data.
+- bpo-21580: Now Tkinter correctly handles bytes arguments passed to Tk. In
+  particular this allows initializing images from binary data.
 
-- Issue #22003: When initialized from a bytes object, io.BytesIO() now
-  defers making a copy until it is mutated, improving performance and
-  memory use on some use cases.  Patch by David Wilson.
+- bpo-22003: When initialized from a bytes object, io.BytesIO() now defers
+  making a copy until it is mutated, improving performance and memory use on
+  some use cases. Patch by David Wilson.
 
-- Issue #22018: On Windows, signal.set_wakeup_fd() now also supports sockets.
-  side effect is that Python depends to the WinSock library.
+- bpo-22018: On Windows, signal.set_wakeup_fd() now also supports sockets. A
+  side effect is that Python depends to the WinSock library.
 
-- Issue #22054: Add os.get_blocking() and os.set_blocking() functions to get
-  and set the blocking mode of a file descriptor (False if the O_NONBLOCK flag
-  is set, True otherwise). These functions are not available on Windows.
+- bpo-22054: Add os.get_blocking() and os.set_blocking() functions to get
+  and set the blocking mode of a file descriptor (False if the O_NONBLOCK
+  flag is set, True otherwise). These functions are not available on
+  Windows.
 
-- Issue #17172: Make turtledemo start as active on OS X even when run with
-  subprocess.  Patch by Lita Cho.
+- bpo-17172: Make turtledemo start as active on OS X even when run with
+  subprocess. Patch by Lita Cho.
 
-- Issue #21704: Fix build error for _multiprocessing when semaphores
-  are not available.  Patch by Arfrever Frehtes Taifersar Arahesis.
+- bpo-21704: Fix build error for _multiprocessing when semaphores are not
+  available. Patch by Arfrever Frehtes Taifersar Arahesis.
 
-- Issue #20173: Convert sha1, sha256, sha512 and md5 to ArgumentClinic.
-  Patch by Vajrasky Kok.
+- bpo-20173: Convert sha1, sha256, sha512 and md5 to ArgumentClinic. Patch
+  by Vajrasky Kok.
 
 - Fix repr(_socket.socket) on Windows 64-bit: don't fail with OverflowError
   on closed socket. repr(socket.socket) already works fine.
 
-- Issue #22033: Reprs of most Python implemened classes now contain actual
+- bpo-22033: Reprs of most Python implemened classes now contain actual
   class name instead of hardcoded one.
 
-- Issue #21947: The dis module can now disassemble generator-iterator
-  objects based on their gi_code attribute. Patch by Clement Rouault.
+- bpo-21947: The dis module can now disassemble generator-iterator objects
+  based on their gi_code attribute. Patch by Clement Rouault.
 
-- Issue #16133: The asynchat.async_chat.handle_read() method now ignores
+- bpo-16133: The asynchat.async_chat.handle_read() method now ignores
   BlockingIOError exceptions.
 
-- Issue #22044: Fixed premature DECREF in call_tzinfo_method.
-  Patch by Tom Flanagan.
+- bpo-22044: Fixed premature DECREF in call_tzinfo_method. Patch by Tom
+  Flanagan.
 
-- Issue #19884: readline: Disable the meta modifier key if stdout is not
-  terminal to not write the ANSI sequence ``"\033[1034h"`` into stdout. This
+- bpo-19884: readline: Disable the meta modifier key if stdout is not a
+  terminal to not write the ANSI sequence ``"\033[1034h"`` into stdout. This
   sequence is used on some terminal (ex: TERM=xterm-256color") to enable
   support of 8 bit characters.
 
-- Issue #4350: Removed a number of out-of-dated and non-working for a long time
+- bpo-4350: Removed a number of out-of-dated and non-working for a long time
   Tkinter methods.
 
-- Issue #6167: Scrollbar.activate() now returns the name of active element if
+- bpo-6167: Scrollbar.activate() now returns the name of active element if
   the argument is not specified.  Scrollbar.set() now always accepts only 2
   arguments.
 
-- Issue #15275: Clean up and speed up the ntpath module.
+- bpo-15275: Clean up and speed up the ntpath module.
 
-- Issue #21888: plistlib's load() and loads() now work if the fmt parameter is
+- bpo-21888: plistlib's load() and loads() now work if the fmt parameter is
   specified.
 
-- Issue #22032: __qualname__ instead of __name__ is now always used to format
+- bpo-22032: __qualname__ instead of __name__ is now always used to format
   fully qualified class names of Python implemented classes.
 
-- Issue #22031: Reprs now always use hexadecimal format with the "0x" prefix
+- bpo-22031: Reprs now always use hexadecimal format with the "0x" prefix
   when contain an id in form " at 0x...".
 
-- Issue #22018: signal.set_wakeup_fd() now raises an OSError instead of a
+- bpo-22018: signal.set_wakeup_fd() now raises an OSError instead of a
   ValueError on ``fstat()`` failure.
 
-- Issue #21044: tarfile.open() now handles fileobj with an integer 'name'
+- bpo-21044: tarfile.open() now handles fileobj with an integer 'name'
   attribute.  Based on patch by Antoine Pietri.
 
-- Issue #21966: Respect -q command-line option when code module is ran.
+- bpo-21966: Respect -q command-line option when code module is ran.
 
-- Issue #19076: Don't pass the redundant 'file' argument to self.error().
+- bpo-19076: Don't pass the redundant 'file' argument to self.error().
 
-- Issue #16382: Improve exception message of warnings.warn() for bad
-  category. Initial patch by Phil Elson.
+- bpo-16382: Improve exception message of warnings.warn() for bad category.
+  Initial patch by Phil Elson.
 
-- Issue #21932: os.read() now uses a :c:func:`Py_ssize_t` type instead of
+- bpo-21932: os.read() now uses a :c:func:`Py_ssize_t` type instead of
   :c:type:`int` for the size to support reading more than 2 GB at once. On
   Windows, the size is truncted to INT_MAX. As any call to os.read(), the OS
   may read less bytes than the number of requested bytes.
 
-- Issue #21942: Fixed source file viewing in pydoc's server mode on Windows.
+- bpo-21942: Fixed source file viewing in pydoc's server mode on Windows.
 
-- Issue #11259: asynchat.async_chat().set_terminator() now raises a ValueError
+- bpo-11259: asynchat.async_chat().set_terminator() now raises a ValueError
   if the number of received bytes is negative.
 
-- Issue #12523: asynchat.async_chat.push() now raises a TypeError if it doesn't
+- bpo-12523: asynchat.async_chat.push() now raises a TypeError if it doesn't
   get a bytes string
 
-- Issue #21707: Add missing kwonlyargcount argument to
+- bpo-21707: Add missing kwonlyargcount argument to
   ModuleFinder.replace_paths_in_code().
 
-- Issue #20639: calling Path.with_suffix('') allows removing the suffix
-  again.  Patch by July Tikhonov.
+- bpo-20639: calling Path.with_suffix('') allows removing the suffix again.
+  Patch by July Tikhonov.
 
-- Issue #21714: Disallow the construction of invalid paths using
+- bpo-21714: Disallow the construction of invalid paths using
   Path.with_name().  Original patch by Antony Lee.
 
-- Issue #15014: Added 'auth' method to smtplib to make implementing auth
+- bpo-15014: Added 'auth' method to smtplib to make implementing auth
   mechanisms simpler, and used it internally in the login method.
 
-- Issue #21151: Fixed a segfault in the winreg module when ``None`` is passed
+- bpo-21151: Fixed a segfault in the winreg module when ``None`` is passed
   as a ``REG_BINARY`` value to SetValueEx.  Patch by John Ehresman.
 
-- Issue #21090: io.FileIO.readall() does not ignore I/O errors anymore. Before,
+- bpo-21090: io.FileIO.readall() does not ignore I/O errors anymore. Before,
   it ignored I/O errors if at least the first C call read() succeed.
 
-- Issue #5800: headers parameter of wsgiref.headers.Headers is now optional.
+- bpo-5800: headers parameter of wsgiref.headers.Headers is now optional.
   Initial patch by Pablo Torres Navarrete and SilentGhost.
 
-- Issue #21781: ssl.RAND_add() now supports strings longer than 2 GB.
+- bpo-21781: ssl.RAND_add() now supports strings longer than 2 GB.
 
-- Issue #21679: Prevent extraneous fstat() calls during open().  Patch by
+- bpo-21679: Prevent extraneous fstat() calls during open().  Patch by
   Bohuslav Kabrda.
 
-- Issue #21863: cProfile now displays the module name of C extension functions,
+- bpo-21863: cProfile now displays the module name of C extension functions,
   in addition to their own name.
 
-- Issue #11453: asyncore: emit a ResourceWarning when an unclosed file_wrapper
+- bpo-11453: asyncore: emit a ResourceWarning when an unclosed file_wrapper
   object is destroyed. The destructor now closes the file if needed. The
   close() method can now be called twice: the second call does nothing.
 
-- Issue #21858: Better handling of Python exceptions in the sqlite3 module.
+- bpo-21858: Better handling of Python exceptions in the sqlite3 module.
 
-- Issue #21476: Make sure the email.parser.BytesParser TextIOWrapper is
+- bpo-21476: Make sure the email.parser.BytesParser TextIOWrapper is
   discarded after parsing, so the input file isn't unexpectedly closed.
 
-- Issue #20295: imghdr now recognizes OpenEXR format images.
+- bpo-20295: imghdr now recognizes OpenEXR format images.
 
-- Issue #21729: Used the "with" statement in the dbm.dumb module to ensure
-  files closing.  Patch by Claudiu Popa.
+- bpo-21729: Used the "with" statement in the dbm.dumb module to ensure
+  files closing. Patch by Claudiu Popa.
 
-- Issue #21491: socketserver: Fix a race condition in child processes reaping.
+- bpo-21491: socketserver: Fix a race condition in child processes reaping.
 
-- Issue #21719: Added the ``st_file_attributes`` field to os.stat_result on
+- bpo-21719: Added the ``st_file_attributes`` field to os.stat_result on
   Windows.
 
-- Issue #21832: Require named tuple inputs to be exact strings.
+- bpo-21832: Require named tuple inputs to be exact strings.
 
-- Issue #21722: The distutils "upload" command now exits with a non-zero
-  return code when uploading fails.  Patch by Martin Dengler.
+- bpo-21722: The distutils "upload" command now exits with a non-zero return
+  code when uploading fails.  Patch by Martin Dengler.
 
-- Issue #21723: asyncio.Queue: support any type of number (ex: float) for the
+- bpo-21723: asyncio.Queue: support any type of number (ex: float) for the
   maximum size. Patch written by Vajrasky Kok.
 
-- Issue #21711: support for "site-python" directories has now been removed
-  from the site module (it was deprecated in 3.4).
+- bpo-21711: support for "site-python" directories has now been removed from
+  the site module (it was deprecated in 3.4).
 
-- Issue #17552: new socket.sendfile() method allowing a file to be sent over a
-  socket by using high-performance os.sendfile() on UNIX.
-  Patch by Giampaolo Rodola'.
+- bpo-17552: new socket.sendfile() method allowing a file to be sent over a
+  socket by using high-performance os.sendfile() on UNIX. Patch by Giampaolo
+  Rodola'.
 
-- Issue #18039: dbm.dump.open() now always creates a new database when the
-  flag has the value 'n'.  Patch by Claudiu Popa.
+- bpo-18039: dbm.dump.open() now always creates a new database when the flag
+  has the value 'n'.  Patch by Claudiu Popa.
 
-- Issue #21326: Add a new is_closed() method to asyncio.BaseEventLoop.
-  run_forever() and run_until_complete() methods of asyncio.BaseEventLoop now
-  raise an exception if the event loop was closed.
+- bpo-21326: Add a new is_closed() method to asyncio.BaseEventLoop.
+  run_forever() and run_until_complete() methods of asyncio.BaseEventLoop
+  now raise an exception if the event loop was closed.
 
-- Issue #21766: Prevent a security hole in CGIHTTPServer by URL unquoting paths
+- bpo-21766: Prevent a security hole in CGIHTTPServer by URL unquoting paths
   before checking for a CGI script at that path.
 
-- Issue #21310: Fixed possible resource leak in failed open().
+- bpo-21310: Fixed possible resource leak in failed open().
 
-- Issue #21256: Printout of keyword args should be in deterministic order in
-  mock function call. This will help to write better doctests.
+- bpo-21256: Printout of keyword args should be in deterministic order in a
+  mock function call. This will help to write better doctests.
 
-- Issue #21677: Fixed chaining nonnormalized exceptions in io close() methods.
+- bpo-21677: Fixed chaining nonnormalized exceptions in io close() methods.
 
-- Issue #11709: Fix the pydoc.help function to not fail when sys.stdin is not a
+- bpo-11709: Fix the pydoc.help function to not fail when sys.stdin is not a
   valid file.
 
-- Issue #21515: tempfile.TemporaryFile now uses os.O_TMPFILE flag is available.
+- bpo-21515: tempfile.TemporaryFile now uses os.O_TMPFILE flag is available.
 
-- Issue #13223: Fix pydoc.writedoc so that the HTML documentation for methods
+- bpo-13223: Fix pydoc.writedoc so that the HTML documentation for methods
   that use 'self' in the example code is generated correctly.
 
-- Issue #21463: In urllib.request, fix pruning of the FTP cache.
+- bpo-21463: In urllib.request, fix pruning of the FTP cache.
 
-- Issue #21618: The subprocess module could fail to close open fds that were
+- bpo-21618: The subprocess module could fail to close open fds that were
   inherited by the calling process and already higher than POSIX resource
   limits would otherwise allow.  On systems with a functioning /proc/self/fd
   or /dev/fd interface the max is now ignored and all fds are closed.
 
-- Issue #20383: Introduce importlib.util.module_from_spec() as the preferred way
-  to create a new module.
+- bpo-20383: Introduce importlib.util.module_from_spec() as the preferred
+  way to create a new module.
 
-- Issue #21552: Fixed possible integer overflow of too long string lengths in
+- bpo-21552: Fixed possible integer overflow of too long string lengths in
   the tkinter module on 64-bit platforms.
 
-- Issue #14315: The zipfile module now ignores extra fields in the central
-  directory that are too short to be parsed instead of letting a struct.unpack
-  error bubble up as this "bad data" appears in many real world zip files in
-  the wild and is ignored by other zip tools.
+- bpo-14315: The zipfile module now ignores extra fields in the central
+  directory that are too short to be parsed instead of letting a
+  struct.unpack error bubble up as this "bad data" appears in many real
+  world zip files in the wild and is ignored by other zip tools.
 
-- Issue #13742: Added "key" and "reverse" parameters to heapq.merge().
-  (First draft of patch contributed by Simon Sapin.)
+- bpo-13742: Added "key" and "reverse" parameters to heapq.merge(). (First
+  draft of patch contributed by Simon Sapin.)
 
-- Issue #21402: tkinter.ttk now works when default root window is not set.
+- bpo-21402: tkinter.ttk now works when default root window is not set.
 
-- Issue #3015: _tkinter.create() now creates tkapp object with wantobject=1 by
+- bpo-3015: _tkinter.create() now creates tkapp object with wantobject=1 by
   default.
 
-- Issue #10203: sqlite3.Row now truly supports sequence protocol.  In particular
-  it supports reverse() and negative indices.  Original patch by Claudiu Popa.
+- bpo-10203: sqlite3.Row now truly supports sequence protocol.  In
+  particular it supports reverse() and negative indices.  Original patch by
+  Claudiu Popa.
 
-- Issue #18807: If copying (no symlinks) specified for a venv, then the python
-  interpreter aliases (python, python3) are now created by copying rather than
-  symlinking.
+- bpo-18807: If copying (no symlinks) specified for a venv, then the python
+  interpreter aliases (python, python3) are now created by copying rather
+  than symlinking.
 
-- Issue #20197: Added support for the WebP image type in the imghdr module.
+- bpo-20197: Added support for the WebP image type in the imghdr module.
   Patch by Fabrice Aneche and Claudiu Popa.
 
-- Issue #21513: Speedup some properties of IP addresses (IPv4Address,
+- bpo-21513: Speedup some properties of IP addresses (IPv4Address,
   IPv6Address) such as .is_private or .is_multicast.
 
-- Issue #21137: Improve the repr for threading.Lock() and its variants
-  by showing the "locked" or "unlocked" status.  Patch by Berker Peksag.
+- bpo-21137: Improve the repr for threading.Lock() and its variants by
+  showing the "locked" or "unlocked" status.  Patch by Berker Peksag.
 
-- Issue #21538: The plistlib module now supports loading of binary plist files
+- bpo-21538: The plistlib module now supports loading of binary plist files
   when reference or offset size is not a power of two.
 
-- Issue #21455: Add a default backlog to socket.listen().
+- bpo-21455: Add a default backlog to socket.listen().
 
-- Issue #21525: Most Tkinter methods which accepted tuples now accept lists too.
+- bpo-21525: Most Tkinter methods which accepted tuples now accept lists
+  too.
 
-- Issue #22166: With the assistance of a new internal _codecs._forget_codec
+- bpo-22166: With the assistance of a new internal _codecs._forget_codec
   helping function, test_codecs now clears the encoding caches to avoid the
   appearance of a reference leak
 
-- Issue #22236: Tkinter tests now don't reuse default root window.  New root
+- bpo-22236: Tkinter tests now don't reuse default root window.  New root
   window is created for every test class.
 
-- Issue #10744: Fix PEP 3118 format strings on ctypes objects with a nontrivial
+- bpo-10744: Fix PEP 3118 format strings on ctypes objects with a nontrivial
   shape.
 
-- Issue #20826: Optimize ipaddress.collapse_addresses().
+- bpo-20826: Optimize ipaddress.collapse_addresses().
 
-- Issue #21487: Optimize ipaddress.summarize_address_range() and
+- bpo-21487: Optimize ipaddress.summarize_address_range() and
   ipaddress.{IPv4Network,IPv6Network}.subnets().
 
-- Issue #21486: Optimize parsing of netmasks in ipaddress.IPv4Network and
+- bpo-21486: Optimize parsing of netmasks in ipaddress.IPv4Network and
   ipaddress.IPv6Network.
 
-- Issue #13916: Disallowed the surrogatepass error handler for non UTF-\*
+- bpo-13916: Disallowed the surrogatepass error handler for non UTF-\*
   encodings.
 
-- Issue #20998: Fixed re.fullmatch() of repeated single character pattern
-  with ignore case.  Original patch by Matthew Barnett.
+- bpo-20998: Fixed re.fullmatch() of repeated single character pattern with
+  ignore case. Original patch by Matthew Barnett.
 
-- Issue #21075: fileinput.FileInput now reads bytes from standard stream if
+- bpo-21075: fileinput.FileInput now reads bytes from standard stream if
   binary mode is specified.  Patch by Sam Kimbrel.
 
-- Issue #19775: Add a samefile() method to pathlib Path objects.  Initial
-  patch by Vajrasky Kok.
+- bpo-19775: Add a samefile() method to pathlib Path objects.  Initial patch
+  by Vajrasky Kok.
 
-- Issue #21226: Set up modules properly in PyImport_ExecCodeModuleObject
-  (and friends).
+- bpo-21226: Set up modules properly in PyImport_ExecCodeModuleObject (and
+  friends).
 
-- Issue #21398: Fix a unicode error in the pydoc pager when the documentation
+- bpo-21398: Fix a unicode error in the pydoc pager when the documentation
   contains characters not encodable to the stdout encoding.
 
-- Issue #16531: ipaddress.IPv4Network and ipaddress.IPv6Network now accept
-  an (address, netmask) tuple argument, so as to easily construct network
+- bpo-16531: ipaddress.IPv4Network and ipaddress.IPv6Network now accept an
+  (address, netmask) tuple argument, so as to easily construct network
   objects from existing addresses.
 
-- Issue #21156: importlib.abc.InspectLoader.source_to_code() is now a
+- bpo-21156: importlib.abc.InspectLoader.source_to_code() is now a
   staticmethod.
 
-- Issue #21424: Simplified and optimized heaqp.nlargest() and nmsmallest()
-  to make fewer tuple comparisons.
+- bpo-21424: Simplified and optimized heaqp.nlargest() and nmsmallest() to
+  make fewer tuple comparisons.
 
-- Issue #21396: Fix TextIOWrapper(..., write_through=True) to not force a
+- bpo-21396: Fix TextIOWrapper(..., write_through=True) to not force a
   flush() on the underlying binary stream.  Patch by akira.
 
-- Issue #18314: Unlink now removes junctions on Windows. Patch by Kim Gräsman
+- bpo-18314: Unlink now removes junctions on Windows. Patch by Kim Gräsman
 
-- Issue #21088: Bugfix for curses.window.addch() regression in 3.4.0.
-  In porting to Argument Clinic, the first two arguments were reversed.
+- bpo-21088: Bugfix for curses.window.addch() regression in 3.4.0. In
+  porting to Argument Clinic, the first two arguments were reversed.
 
-- Issue #21407: _decimal: The module now supports function signatures.
+- bpo-21407: _decimal: The module now supports function signatures.
 
-- Issue #10650: Remove the non-standard 'watchexp' parameter from the
+- bpo-10650: Remove the non-standard 'watchexp' parameter from the
   Decimal.quantize() method in the Python version.  It had never been
   present in the C version.
 
-- Issue #21469: Reduced the risk of false positives in robotparser by
-  checking to make sure that robots.txt has been read or does not exist
-  prior to returning True in can_fetch().
+- bpo-21469: Reduced the risk of false positives in robotparser by checking
+  to make sure that robots.txt has been read or does not exist prior to
+  returning True in can_fetch().
 
-- Issue #19414: Have the OrderedDict mark deleted links as unusable.
-  This gives an early failure if the link is deleted during iteration.
+- bpo-19414: Have the OrderedDict mark deleted links as unusable. This gives
+  an early failure if the link is deleted during iteration.
 
-- Issue #21421: Add __slots__ to the MappingViews ABC.
-  Patch by Josh Rosenberg.
+- bpo-21421: Add __slots__ to the MappingViews ABC. Patch by Josh Rosenberg.
 
-- Issue #21101: Eliminate double hashing in the C speed-up code for
+- bpo-21101: Eliminate double hashing in the C speed-up code for
   collections.Counter().
 
-- Issue #21321: itertools.islice() now releases the reference to the source
+- bpo-21321: itertools.islice() now releases the reference to the source
   iterator when the slice is exhausted.  Patch by Anton Afanasyev.
 
-- Issue #21057: TextIOWrapper now allows the underlying binary stream's
-  read() or read1() method to return an arbitrary bytes-like object
-  (such as a memoryview).  Patch by Nikolaus Rath.
+- bpo-21057: TextIOWrapper now allows the underlying binary stream's read()
+  or read1() method to return an arbitrary bytes-like object (such as a
+  memoryview). Patch by Nikolaus Rath.
 
-- Issue #20951: SSLSocket.send() now raises either SSLWantReadError or
+- bpo-20951: SSLSocket.send() now raises either SSLWantReadError or
   SSLWantWriteError on a non-blocking socket if the operation would block.
   Previously, it would return 0.  Patch by Nikolaus Rath.
 
-- Issue #13248: removed previously deprecated asyncore.dispatcher __getattr__
+- bpo-13248: removed previously deprecated asyncore.dispatcher __getattr__
   cheap inheritance hack.
 
-- Issue #9815: assertRaises now tries to clear references to local variables
-  in the exception's traceback.
+- bpo-9815: assertRaises now tries to clear references to local variables in
+  the exception's traceback.
 
-- Issue #19940: ssl.cert_time_to_seconds() now interprets the given time
-  string in the UTC timezone (as specified in RFC 5280), not the local
-  timezone.
+- bpo-19940: ssl.cert_time_to_seconds() now interprets the given time string
+  in the UTC timezone (as specified in RFC 5280), not the local timezone.
 
-- Issue #13204: Calling sys.flags.__new__ would crash the interpreter,
-  now it raises a TypeError.
+- bpo-13204: Calling sys.flags.__new__ would crash the interpreter, now it
+  raises a TypeError.
 
-- Issue #19385: Make operations on a closed dbm.dumb database always raise the
+- bpo-19385: Make operations on a closed dbm.dumb database always raise the
   same exception.
 
-- Issue #21207: Detect when the os.urandom cached fd has been closed or
+- bpo-21207: Detect when the os.urandom cached fd has been closed or
   replaced, and open it anew.
 
-- Issue #21291: subprocess's Popen.wait() is now thread safe so that
-  multiple threads may be calling wait() or poll() on a Popen instance
-  at the same time without losing the Popen.returncode value.
+- bpo-21291: subprocess's Popen.wait() is now thread safe so that multiple
+  threads may be calling wait() or poll() on a Popen instance at the same
+  time without losing the Popen.returncode value.
 
-- Issue #21127: Path objects can now be instantiated from str subclass
+- bpo-21127: Path objects can now be instantiated from str subclass
   instances (such as ``numpy.str_``).
 
-- Issue #15002: urllib.response object to use _TemporaryFileWrapper (and
+- bpo-15002: urllib.response object to use _TemporaryFileWrapper (and
   _TemporaryFileCloser) facility. Provides a better way to handle file
   descriptor close. Patch contributed by Christian Theune.
 
-- Issue #12220: mindom now raises a custom ValueError indicating it doesn't
+- bpo-12220: mindom now raises a custom ValueError indicating it doesn't
   support spaces in URIs instead of letting a 'split' ValueError bubble up.
 
-- Issue #21068: The ssl.PROTOCOL* constants are now enum members.
+- bpo-21068: The ssl.PROTOCOL* constants are now enum members.
 
-- Issue #21276: posixmodule: Don't define USE_XATTRS on KFreeBSD and the Hurd.
+- bpo-21276: posixmodule: Don't define USE_XATTRS on KFreeBSD and the Hurd.
 
-- Issue #21262: New method assert_not_called for Mock.
-  It raises AssertionError if the mock has been called.
+- bpo-21262: New method assert_not_called for Mock. It raises AssertionError
+  if the mock has been called.
 
-- Issue #21238: New keyword argument `unsafe` to Mock. It raises
+- bpo-21238: New keyword argument `unsafe` to Mock. It raises
   `AttributeError` incase of an attribute startswith assert or assret.
 
-- Issue #20896: ssl.get_server_certificate() now uses PROTOCOL_SSLv23, not
+- bpo-20896: ssl.get_server_certificate() now uses PROTOCOL_SSLv23, not
   PROTOCOL_SSLv3, for maximum compatibility.
 
-- Issue #21239: patch.stopall() didn't work deterministically when the same
+- bpo-21239: patch.stopall() didn't work deterministically when the same
   name was patched more than once.
 
-- Issue #21203: Updated fileConfig and dictConfig to remove inconsistencies.
+- bpo-21203: Updated fileConfig and dictConfig to remove inconsistencies.
   Thanks to Jure Koren for the patch.
 
-- Issue #21222: Passing name keyword argument to mock.create_autospec now
+- bpo-21222: Passing name keyword argument to mock.create_autospec now
   works.
 
-- Issue #21197: Add lib64 -> lib symlink in venvs on 64-bit non-OS X POSIX.
+- bpo-21197: Add lib64 -> lib symlink in venvs on 64-bit non-OS X POSIX.
 
-- Issue #17498: Some SMTP servers disconnect after certain errors, violating
-  strict RFC conformance.  Instead of losing the error code when we issue the
-  subsequent RSET, smtplib now returns the error code and defers raising the
-  SMTPServerDisconnected error until the next command is issued.
+- bpo-17498: Some SMTP servers disconnect after certain errors, violating
+  strict RFC conformance.  Instead of losing the error code when we issue
+  the subsequent RSET, smtplib now returns the error code and defers raising
+  the SMTPServerDisconnected error until the next command is issued.
 
-- Issue #17826: setting an iterable side_effect on a mock function created by
+- bpo-17826: setting an iterable side_effect on a mock function created by
   create_autospec now works. Patch by Kushal Das.
 
-- Issue #7776: Fix ``Host:`` header and reconnection when using
+- bpo-7776: Fix ``Host:`` header and reconnection when using
   http.client.HTTPConnection.set_tunnel(). Patch by Nikolaus Rath.
 
-- Issue #20968: unittest.mock.MagicMock now supports division.
-  Patch by Johannes Baiter.
+- bpo-20968: unittest.mock.MagicMock now supports division. Patch by
+  Johannes Baiter.
 
-- Issue #21529 (CVE-2014-4616): Fix arbitrary memory access in
-  JSONDecoder.raw_decode with a negative second parameter. Bug reported by Guido
-  Vranken.
+- bpo-21529: Fix arbitrary memory access in JSONDecoder.raw_decode with a
+  negative second parameter. Bug reported by Guido Vranken. (See also:
+  CVE-2014-4616)
 
-- Issue #21169: getpass now handles non-ascii characters that the
-  input stream encoding cannot encode by re-encoding using the
-  replace error handler.
+- bpo-21169: getpass now handles non-ascii characters that the input stream
+  encoding cannot encode by re-encoding using the replace error handler.
 
-- Issue #21171: Fixed undocumented filter API of the rot13 codec.
-  Patch by Berker Peksag.
+- bpo-21171: Fixed undocumented filter API of the rot13 codec. Patch by
+  Berker Peksag.
 
-- Issue #20539: Improved math.factorial error message for large positive inputs
-  and changed exception type (OverflowError -> ValueError) for large negative
-  inputs.
+- bpo-20539: Improved math.factorial error message for large positive inputs
+  and changed exception type (OverflowError -> ValueError) for large
+  negative inputs.
 
-- Issue #21172: isinstance check relaxed from dict to collections.Mapping.
+- bpo-21172: isinstance check relaxed from dict to collections.Mapping.
 
-- Issue #21155: asyncio.EventLoop.create_unix_server() now raises a ValueError
+- bpo-21155: asyncio.EventLoop.create_unix_server() now raises a ValueError
   if path and sock are specified at the same time.
 
-- Issue #21136: Avoid unnecessary normalization of Fractions resulting from
+- bpo-21136: Avoid unnecessary normalization of Fractions resulting from
   power and other operations.  Patch by Raymond Hettinger.
 
-- Issue #17621: Introduce importlib.util.LazyLoader.
+- bpo-17621: Introduce importlib.util.LazyLoader.
 
-- Issue #21076: signal module constants were turned into enums.
-  Patch by Giampaolo Rodola'.
+- bpo-21076: signal module constants were turned into enums. Patch by
+  Giampaolo Rodola'.
 
-- Issue #20636: Improved the repr of Tkinter widgets.
+- bpo-20636: Improved the repr of Tkinter widgets.
 
-- Issue #19505: The items, keys, and values views of OrderedDict now support
+- bpo-19505: The items, keys, and values views of OrderedDict now support
   reverse iteration using reversed().
 
-- Issue #21149: Improved thread-safety in logging cleanup during interpreter
+- bpo-21149: Improved thread-safety in logging cleanup during interpreter
   shutdown. Thanks to Devin Jeanpierre for the patch.
 
-- Issue #21058: Fix a leak of file descriptor in
+- bpo-21058: Fix a leak of file descriptor in
   :func:`tempfile.NamedTemporaryFile`, close the file descriptor if
   :func:`io.open` fails
 
-- Issue #21200: Return None from pkgutil.get_loader() when __spec__ is missing.
+- bpo-21200: Return None from pkgutil.get_loader() when __spec__ is missing.
 
-- Issue #21013: Enhance ssl.create_default_context() when used for server side
+- bpo-21013: Enhance ssl.create_default_context() when used for server side
   sockets to provide better security by default.
 
-- Issue #20145: `assertRaisesRegex` and `assertWarnsRegex` now raise a
+- bpo-20145: `assertRaisesRegex` and `assertWarnsRegex` now raise a
   TypeError if the second argument is not a string or compiled regex.
 
-- Issue #20633: Replace relative import by absolute import.
+- bpo-20633: Replace relative import by absolute import.
 
-- Issue #20980: Stop wrapping exception when using ThreadPool.
+- bpo-20980: Stop wrapping exception when using ThreadPool.
 
-- Issue #21082: In os.makedirs, do not set the process-wide umask. Note this
+- bpo-21082: In os.makedirs, do not set the process-wide umask. Note this
   changes behavior of makedirs when exist_ok=True.
 
-- Issue #20990: Fix issues found by pyflakes for multiprocessing.
+- bpo-20990: Fix issues found by pyflakes for multiprocessing.
 
-- Issue #21015: SSL contexts will now automatically select an elliptic
-  curve for ECDH key exchange on OpenSSL 1.0.2 and later, and otherwise
-  default to "prime256v1".
+- bpo-21015: SSL contexts will now automatically select an elliptic curve
+  for ECDH key exchange on OpenSSL 1.0.2 and later, and otherwise default to
+  "prime256v1".
 
-- Issue #21000: Improve the command-line interface of json.tool.
+- bpo-21000: Improve the command-line interface of json.tool.
 
-- Issue #20995: Enhance default ciphers used by the ssl module to enable
-  better security and prioritize perfect forward secrecy.
+- bpo-20995: Enhance default ciphers used by the ssl module to enable better
+  security and prioritize perfect forward secrecy.
 
-- Issue #20884: Don't assume that __file__ is defined on importlib.__init__.
+- bpo-20884: Don't assume that __file__ is defined on importlib.__init__.
 
-- Issue #21499: Ignore __builtins__ in several test_importlib.test_api tests.
+- bpo-21499: Ignore __builtins__ in several test_importlib.test_api tests.
 
-- Issue #20627: xmlrpc.client.ServerProxy is now a context manager.
+- bpo-20627: xmlrpc.client.ServerProxy is now a context manager.
 
-- Issue #19165: The formatter module now raises DeprecationWarning instead of
+- bpo-19165: The formatter module now raises DeprecationWarning instead of
   PendingDeprecationWarning.
 
-- Issue #13936: Remove the ability of datetime.time instances to be considered
+- bpo-13936: Remove the ability of datetime.time instances to be considered
   false in boolean contexts.
 
-- Issue #18931: selectors module now supports /dev/poll on Solaris.
-  Patch by Giampaolo Rodola'.
+- bpo-18931: selectors module now supports /dev/poll on Solaris. Patch by
+  Giampaolo Rodola'.
 
-- Issue #19977: When the ``LC_TYPE`` locale is the POSIX locale (``C`` locale),
+- bpo-19977: When the ``LC_TYPE`` locale is the POSIX locale (``C`` locale),
   :py:data:`sys.stdin` and :py:data:`sys.stdout` are now using the
-  ``surrogateescape`` error handler, instead of the ``strict`` error handler.
+  ``surrogateescape`` error handler, instead of the ``strict`` error
+  handler.
 
-- Issue #20574: Implement incremental decoder for cp65001 code (Windows code
+- bpo-20574: Implement incremental decoder for cp65001 code (Windows code
   page 65001, Microsoft UTF-8).
 
-- Issue #20879: Delay the initialization of encoding and decoding tables for
+- bpo-20879: Delay the initialization of encoding and decoding tables for
   base32, ascii85 and base85 codecs in the base64 module, and delay the
-  initialization of the unquote_to_bytes() table of the urllib.parse module, to
-  not waste memory if these modules are not used.
+  initialization of the unquote_to_bytes() table of the urllib.parse module,
+  to not waste memory if these modules are not used.
 
-- Issue #19157: Include the broadcast address in the usuable hosts for IPv6
-  in ipaddress.
+- bpo-19157: Include the broadcast address in the usuable hosts for IPv6 in
+  ipaddress.
 
-- Issue #11599: When an external command (e.g. compiler) fails, distutils now
-  prints out the whole command line (instead of just the command name) if the
-  environment variable DISTUTILS_DEBUG is set.
+- bpo-11599: When an external command (e.g. compiler) fails, distutils now
+  prints out the whole command line (instead of just the command name) if
+  the environment variable DISTUTILS_DEBUG is set.
 
-- Issue #4931: distutils should not produce unhelpful "error: None" messages
-  anymore.  distutils.util.grok_environment_error is kept but doc-deprecated.
+- bpo-4931: distutils should not produce unhelpful "error: None" messages
+  anymore. distutils.util.grok_environment_error is kept but doc-deprecated.
 
-- Issue #20875: Prevent possible gzip "'read' is not defined" NameError.
-  Patch by Claudiu Popa.
+- bpo-20875: Prevent possible gzip "'read' is not defined" NameError. Patch
+  by Claudiu Popa.
 
-- Issue #11558: ``email.message.Message.attach`` now returns a more
-  useful error message if ``attach`` is called on a message for which
+- bpo-11558: ``email.message.Message.attach`` now returns a more useful
+  error message if ``attach`` is called on a message for which
   ``is_multipart`` is False.
 
-- Issue #20283: RE pattern methods now accept the string keyword parameters
-  as documented.  The pattern and source keyword parameters are left as
+- bpo-20283: RE pattern methods now accept the string keyword parameters as
+  documented. The pattern and source keyword parameters are left as
   deprecated aliases.
 
-- Issue #20778: Fix modulefinder to work with bytecode-only modules.
+- bpo-20778: Fix modulefinder to work with bytecode-only modules.
 
-- Issue #20791: copy.copy() now doesn't make a copy when the input is
-  a bytes object.  Initial patch by Peter Otten.
+- bpo-20791: copy.copy() now doesn't make a copy when the input is a bytes
+  object. Initial patch by Peter Otten.
 
-- Issue #19748: On AIX, time.mktime() now raises an OverflowError for year
+- bpo-19748: On AIX, time.mktime() now raises an OverflowError for year
   outsize range [1902; 2037].
 
-- Issue #19573: inspect.signature: Use enum for parameter kind constants.
+- bpo-19573: inspect.signature: Use enum for parameter kind constants.
 
-- Issue #20726: inspect.signature: Make Signature and Parameter picklable.
+- bpo-20726: inspect.signature: Make Signature and Parameter picklable.
 
-- Issue #17373: Add inspect.Signature.from_callable method.
+- bpo-17373: Add inspect.Signature.from_callable method.
 
-- Issue #20378: Improve repr of inspect.Signature and inspect.Parameter.
+- bpo-20378: Improve repr of inspect.Signature and inspect.Parameter.
 
-- Issue #20816: Fix inspect.getcallargs() to raise correct TypeError for
-  missing keyword-only arguments. Patch by Jeremiah Lowin.
+- bpo-20816: Fix inspect.getcallargs() to raise correct TypeError for
+  missing keyword- only arguments. Patch by Jeremiah Lowin.
 
-- Issue #20817: Fix inspect.getcallargs() to fail correctly if more
-  than 3 arguments are missing. Patch by Jeremiah Lowin.
+- bpo-20817: Fix inspect.getcallargs() to fail correctly if more than 3
+  arguments are missing. Patch by Jeremiah Lowin.
 
-- Issue #6676: Ensure a meaningful exception is raised when attempting
-  to parse more than one XML document per pyexpat xmlparser instance.
-  (Original patches by Hirokazu Yamamoto and Amaury Forgeot d'Arc, with
-  suggested wording by David Gutteridge)
+- bpo-6676: Ensure a meaningful exception is raised when attempting to parse
+  more than one XML document per pyexpat xmlparser instance. (Original
+  patches by Hirokazu Yamamoto and Amaury Forgeot d'Arc, with suggested
+  wording by David Gutteridge)
 
-- Issue #21117: Fix inspect.signature to better support functools.partial.
-  Due to the specifics of functools.partial implementation,
-  positional-or-keyword arguments passed as keyword arguments become
-  keyword-only.
+- bpo-21117: Fix inspect.signature to better support functools.partial. Due
+  to the specifics of functools.partial implementation, positional-or-
+  keyword arguments passed as keyword arguments become keyword-only.
 
-- Issue #20334: inspect.Signature and inspect.Parameter are now hashable.
+- bpo-20334: inspect.Signature and inspect.Parameter are now hashable.
   Thanks to Antony Lee for bug reports and suggestions.
 
-- Issue #15916: doctest.DocTestSuite returns an empty unittest.TestSuite instead
-  of raising ValueError if it finds no tests
+- bpo-15916: doctest.DocTestSuite returns an empty unittest.TestSuite
+  instead of raising ValueError if it finds no tests
 
-- Issue #21209: Fix asyncio.tasks.CoroWrapper to workaround a bug
-  in yield-from implementation in CPythons prior to 3.4.1.
+- bpo-21209: Fix asyncio.tasks.CoroWrapper to workaround a bug in yield-from
+  implementation in CPythons prior to 3.4.1.
 
-- asyncio: Add gi_{frame,running,code} properties to CoroWrapper
-  (upstream issue #163).
+- asyncio: Add gi_{frame,running,code} properties to CoroWrapper (upstream
+  issue #163).
 
-- Issue #21311: Avoid exception in _osx_support with non-standard compiler
-  configurations.  Patch by John Szakmeister.
+- bpo-21311: Avoid exception in _osx_support with non-standard compiler
+  configurations. Patch by John Szakmeister.
 
-- Issue #11571: Ensure that the turtle window becomes the topmost window
-  when launched on OS X.
+- bpo-11571: Ensure that the turtle window becomes the topmost window when
+  launched on OS X.
 
-- Issue #21801: Validate that __signature__ is None or an instance of Signature.
+- bpo-21801: Validate that __signature__ is None or an instance of
+  Signature.
 
-- Issue #21923: Prevent AttributeError in distutils.sysconfig.customize_compiler
-  due to possible uninitialized _config_vars.
+- bpo-21923: Prevent AttributeError in
+  distutils.sysconfig.customize_compiler due to possible uninitialized
+  _config_vars.
 
-- Issue #21323: Fix http.server to again handle scripts in CGI subdirectories,
+- bpo-21323: Fix http.server to again handle scripts in CGI subdirectories,
   broken by the fix for security issue #19435.  Patch by Zach Byrne.
 
-- Issue #22733: Fix ffi_prep_args not zero-extending argument values correctly
+- bpo-22733: Fix ffi_prep_args not zero-extending argument values correctly
   on 64-bit Windows.
 
-- Issue #23302: Default to TCP_NODELAY=1 upon establishing an HTTPConnection.
-  Removed use of hard-coded MSS as it's an optimization that's no longer needed
-  with Nagle disabled.
+- bpo-23302: Default to TCP_NODELAY=1 upon establishing an HTTPConnection.
+  Removed use of hard-coded MSS as it's an optimization that's no longer
+  needed with Nagle disabled.
 
 IDLE
 ----
 
-- Issue #20577: Configuration of the max line length for the FormatParagraph
-  extension has been moved from the General tab of the Idle preferences dialog
-  to the FormatParagraph tab of the Config Extensions dialog.
-  Patch by Tal Einat.
+- bpo-20577: Configuration of the max line length for the FormatParagraph
+  extension has been moved from the General tab of the Idle preferences
+  dialog to the FormatParagraph tab of the Config Extensions dialog. Patch
+  by Tal Einat.
 
-- Issue #16893: Update Idle doc chapter to match current Idle and add new
+- bpo-16893: Update Idle doc chapter to match current Idle and add new
   information.
 
-- Issue #3068: Add Idle extension configuration dialog to Options menu.
-  Changes are written to HOME/.idlerc/config-extensions.cfg.
-  Original patch by Tal Einat.
+- bpo-3068: Add Idle extension configuration dialog to Options menu. Changes
+  are written to HOME/.idlerc/config-extensions.cfg. Original patch by Tal
+  Einat.
 
-- Issue #16233: A module browser (File : Class Browser, Alt+C) requires an
+- bpo-16233: A module browser (File : Class Browser, Alt+C) requires an
   editor window with a filename.  When Class Browser is requested otherwise,
   from a shell, output window, or 'Untitled' editor, Idle no longer displays
-  an error box.  It now pops up an Open Module box (Alt+M). If a valid name
+  an error box. It now pops up an Open Module box (Alt+M). If a valid name
   is entered and a module is opened, a corresponding browser is also opened.
 
-- Issue #4832: Save As to type Python files automatically adds .py to the
-  name you enter (even if your system does not display it).  Some systems
+- bpo-4832: Save As to type Python files automatically adds .py to the name
+  you enter (even if your system does not display it).  Some systems
   automatically add .txt when type is Text files.
 
-- Issue #21986: Code objects are not normally pickled by the pickle module.
-  To match this, they are no longer pickled when running under Idle.
+- bpo-21986: Code objects are not normally pickled by the pickle module. To
+  match this, they are no longer pickled when running under Idle.
 
-- Issue #17390: Adjust Editor window title; remove 'Python',
-  move version to end.
+- bpo-17390: Adjust Editor window title; remove 'Python', move version to
+  end.
 
-- Issue #14105: Idle debugger breakpoints no longer disappear
-  when inserting or deleting lines.
+- bpo-14105: Idle debugger breakpoints no longer disappear when inserting or
+  deleting lines.
 
-- Issue #17172: Turtledemo can now be run from Idle.
-  Currently, the entry is on the Help menu, but it may move to Run.
-  Patch by Ramchandra Apt and Lita Cho.
+- bpo-17172: Turtledemo can now be run from Idle. Currently, the entry is on
+  the Help menu, but it may move to Run. Patch by Ramchandra Apt and Lita
+  Cho.
 
-- Issue #21765: Add support for non-ascii identifiers to HyperParser.
+- bpo-21765: Add support for non-ascii identifiers to HyperParser.
 
-- Issue #21940: Add unittest for WidgetRedirector. Initial patch by Saimadhav
+- bpo-21940: Add unittest for WidgetRedirector. Initial patch by Saimadhav
   Heblikar.
 
-- Issue #18592: Add unittest for SearchDialogBase. Patch by Phil Webster.
+- bpo-18592: Add unittest for SearchDialogBase. Patch by Phil Webster.
 
-- Issue #21694: Add unittest for ParenMatch. Patch by Saimadhav Heblikar.
+- bpo-21694: Add unittest for ParenMatch. Patch by Saimadhav Heblikar.
 
-- Issue #21686: add unittest for HyperParser. Original patch by Saimadhav
+- bpo-21686: add unittest for HyperParser. Original patch by Saimadhav
   Heblikar.
 
-- Issue #12387: Add missing upper(lower)case versions of default Windows key
-  bindings for Idle so Caps Lock does not disable them. Patch by Roger Serwy.
+- bpo-12387: Add missing upper(lower)case versions of default Windows key
+  bindings for Idle so Caps Lock does not disable them. Patch by Roger
+  Serwy.
 
-- Issue #21695: Closing a Find-in-files output window while the search is
-  still in progress no longer closes Idle.
+- bpo-21695: Closing a Find-in-files output window while the search is still
+  in progress no longer closes Idle.
 
-- Issue #18910: Add unittest for textView. Patch by Phil Webster.
+- bpo-18910: Add unittest for textView. Patch by Phil Webster.
 
-- Issue #18292: Add unittest for AutoExpand. Patch by Saihadhav Heblikar.
+- bpo-18292: Add unittest for AutoExpand. Patch by Saihadhav Heblikar.
 
-- Issue #18409: Add unittest for AutoComplete. Patch by Phil Webster.
+- bpo-18409: Add unittest for AutoComplete. Patch by Phil Webster.
 
-- Issue #21477: htest.py - Improve framework, complete set of tests.
-  Patches by Saimadhav Heblikar
+- bpo-21477: htest.py - Improve framework, complete set of tests. Patches by
+  Saimadhav Heblikar
 
-- Issue #18104: Add idlelib/idle_test/htest.py with a few sample tests to begin
-  consolidating and improving human-validated tests of Idle. Change other files
-  as needed to work with htest.  Running the module as __main__ runs all tests.
+- bpo-18104: Add idlelib/idle_test/htest.py with a few sample tests to begin
+  consolidating and improving human-validated tests of Idle. Change other
+  files as needed to work with htest.  Running the module as __main__ runs
+  all tests.
 
-- Issue #21139: Change default paragraph width to 72, the PEP 8 recommendation.
+- bpo-21139: Change default paragraph width to 72, the PEP 8 recommendation.
 
-- Issue #21284: Paragraph reformat test passes after user changes reformat width.
+- bpo-21284: Paragraph reformat test passes after user changes reformat
+  width.
 
-- Issue #17654: Ensure IDLE menus are customized properly on OS X for
-  non-framework builds and for all variants of Tk.
+- bpo-17654: Ensure IDLE menus are customized properly on OS X for non-
+  framework builds and for all variants of Tk.
 
-- Issue #23180: Rename IDLE "Windows" menu item to "Window".
-  Patch by Al Sweigart.
+- bpo-23180: Rename IDLE "Windows" menu item to "Window". Patch by Al
+  Sweigart.
 
 Build
 -----
 
-- Issue #15506: Use standard PKG_PROG_PKG_CONFIG autoconf macro in the configure
-  script.
+- bpo-15506: Use standard PKG_PROG_PKG_CONFIG autoconf macro in the
+  configure script.
 
-- Issue #22935: Allow the ssl module to be compiled if openssl doesn't support
+- bpo-22935: Allow the ssl module to be compiled if openssl doesn't support
   SSL 3.
 
-- Issue #22592: Drop support of the Borland C compiler to build Python. The
+- bpo-22592: Drop support of the Borland C compiler to build Python. The
   distutils module still supports it to build extensions.
 
-- Issue #22591: Drop support of MS-DOS, especially of the DJGPP compiler
-  (MS-DOS port of GCC).
+- bpo-22591: Drop support of MS-DOS, especially of the DJGPP compiler (MS-
+  DOS port of GCC).
 
-- Issue #16537: Check whether self.extensions is empty in setup.py. Patch by
+- bpo-16537: Check whether self.extensions is empty in setup.py. Patch by
   Jonathan Hosmer.
 
-- Issue #22359: Remove incorrect uses of recursive make.  Patch by Jonas
+- bpo-22359: Remove incorrect uses of recursive make.  Patch by Jonas
   Wagner.
 
-- Issue #21958: Define HAVE_ROUND when building with Visual Studio 2013 and
+- bpo-21958: Define HAVE_ROUND when building with Visual Studio 2013 and
   above.  Patch by Zachary Turner.
 
-- Issue #18093: the programs that embed the CPython runtime are now in a
+- bpo-18093: the programs that embed the CPython runtime are now in a
   separate "Programs" directory, rather than being kept in the Modules
   directory.
 
-- Issue #15759: "make suspicious", "make linkcheck" and "make doctest" in Doc/
+- bpo-15759: "make suspicious", "make linkcheck" and "make doctest" in Doc/
   now display special message when and only when there are failures.
 
-- Issue #21141: The Windows build process no longer attempts to find Perl,
-  instead relying on OpenSSL source being configured and ready to build.  The
-  ``PCbuild\build_ssl.py`` script has been re-written and re-named to
+- bpo-21141: The Windows build process no longer attempts to find Perl,
+  instead relying on OpenSSL source being configured and ready to build.
+  The ``PCbuild\build_ssl.py`` script has been re-written and re-named to
   ``PCbuild\prepare_ssl.py``, and takes care of configuring OpenSSL source
   for both 32 and 64 bit platforms.  OpenSSL sources obtained from
   svn.python.org will always be pre-configured and ready to build.
 
-- Issue #21037: Add a build option to enable AddressSanitizer support.
+- bpo-21037: Add a build option to enable AddressSanitizer support.
 
-- Issue #19962: The Windows build process now creates "python.bat" in the
-  root of the source tree, which passes all arguments through to the most
+- bpo-19962: The Windows build process now creates "python.bat" in the root
+  of the source tree, which passes all arguments through to the most
   recently built interpreter.
 
-- Issue #21285: Refactor and fix curses configure check to always search
-  in a ncursesw directory.
+- bpo-21285: Refactor and fix curses configure check to always search in a
+  ncursesw directory.
 
-- Issue #15234: For BerkelyDB and Sqlite, only add the found library and
-  include directories if they aren't already being searched. This avoids
-  an explicit runtime library dependency.
+- bpo-15234: For BerkelyDB and Sqlite, only add the found library and
+  include directories if they aren't already being searched. This avoids an
+  explicit runtime library dependency.
 
-- Issue #17861: Tools/scripts/generate_opcode_h.py automatically regenerates
+- bpo-17861: Tools/scripts/generate_opcode_h.py automatically regenerates
   Include/opcode.h from Lib/opcode.py if the latter gets any change.
 
-- Issue #20644: OS X installer build support for documentation build changes
-  in 3.4.1: assume externally supplied sphinx-build is available in /usr/bin.
+- bpo-20644: OS X installer build support for documentation build changes in
+  3.4.1: assume externally supplied sphinx-build is available in /usr/bin.
 
-- Issue #20022: Eliminate use of deprecated bundlebuilder in OS X builds.
+- bpo-20022: Eliminate use of deprecated bundlebuilder in OS X builds.
 
-- Issue #15968: Incorporated Tcl, Tk, and Tix builds into the Windows build
+- bpo-15968: Incorporated Tcl, Tk, and Tix builds into the Windows build
   solution.
 
-- Issue #17095: Fix Modules/Setup *shared* support.
+- bpo-17095: Fix Modules/Setup *shared* support.
 
-- Issue #21811: Anticipated fixes to support OS X versions > 10.9.
+- bpo-21811: Anticipated fixes to support OS X versions > 10.9.
 
-- Issue #21166: Prevent possible segfaults and other random failures of
-  python --generate-posix-vars in pybuilddir.txt build target.
+- bpo-21166: Prevent possible segfaults and other random failures of python
+  --generate- posix-vars in pybuilddir.txt build target.
 
-- Issue #18096: Fix library order returned by python-config.
+- bpo-18096: Fix library order returned by python-config.
 
-- Issue #17219: Add library build dir for Python extension cross-builds.
+- bpo-17219: Add library build dir for Python extension cross-builds.
 
-- Issue #22919: Windows build updated to support VC 14.0 (Visual Studio 2015),
+- bpo-22919: Windows build updated to support VC 14.0 (Visual Studio 2015),
   which will be used for the official release.
 
-- Issue #21236: Build _msi.pyd with cabinet.lib instead of fci.lib
+- bpo-21236: Build _msi.pyd with cabinet.lib instead of fci.lib
 
-- Issue #17128: Use private version of OpenSSL for OS X 10.5+ installer.
+- bpo-17128: Use private version of OpenSSL for OS X 10.5+ installer.
 
 C API
 -----
 
-- Issue #14203: Remove obsolete support for view==NULL in PyBuffer_FillInfo(),
+- bpo-14203: Remove obsolete support for view==NULL in PyBuffer_FillInfo(),
   bytearray_getbuffer(), bytesiobuf_getbuffer() and array_buffer_getbuf().
   All functions now raise BufferError in that case.
 
-- Issue #22445: PyBuffer_IsContiguous() now implements precise contiguity
+- bpo-22445: PyBuffer_IsContiguous() now implements precise contiguity
   tests, compatible with NumPy's NPY_RELAXED_STRIDES_CHECKING compilation
   flag.  Previously the function reported false negatives for corner cases.
 
-- Issue #22079: PyType_Ready() now checks that statically allocated type has
-  no dynamically allocated bases.
+- bpo-22079: PyType_Ready() now checks that statically allocated type has no
+  dynamically allocated bases.
 
-- Issue #22453: Removed non-documented macro PyObject_REPR().
+- bpo-22453: Removed non-documented macro PyObject_REPR().
 
-- Issue #18395: Rename ``_Py_char2wchar()`` to :c:func:`Py_DecodeLocale`,
+- bpo-18395: Rename ``_Py_char2wchar()`` to :c:func:`Py_DecodeLocale`,
   rename ``_Py_wchar2char()`` to :c:func:`Py_EncodeLocale`, and document
   these functions.
 
-- Issue #21233: Add new C functions: PyMem_RawCalloc(), PyMem_Calloc(),
+- bpo-21233: Add new C functions: PyMem_RawCalloc(), PyMem_Calloc(),
   PyObject_Calloc(), _PyObject_GC_Calloc(). bytes(int) is now using
   ``calloc()`` instead of ``malloc()`` for large objects which is faster and
   use less memory.
 
-- Issue #20942: PyImport_ImportFrozenModuleObject() no longer sets __file__ to
+- bpo-20942: PyImport_ImportFrozenModuleObject() no longer sets __file__ to
   match what importlib does; this affects _frozen_importlib as well as any
   module loaded using imp.init_frozen().
 
 Documentation
 -------------
 
-- Issue #19548: Update the codecs module documentation to better cover the
+- bpo-19548: Update the codecs module documentation to better cover the
   distinction between text encodings and other codecs, together with other
   clarifications. Patch by Martin Panter.
 
-- Issue #22394: Doc/Makefile now supports ``make venv PYTHON=../python`` to
-  create a venv for generating the documentation, e.g.,
-  ``make html PYTHON=venv/bin/python3``.
+- bpo-22394: Doc/Makefile now supports ``make venv PYTHON=../python`` to
+  create a venv for generating the documentation, e.g., ``make html
+  PYTHON=venv/bin/python3``.
 
-- Issue #21514: The documentation of the json module now refers to new JSON RFC
+- bpo-21514: The documentation of the json module now refers to new JSON RFC
   7159 instead of obsoleted RFC 4627.
 
-- Issue #21777: The binary sequence methods on bytes and bytearray are now
+- bpo-21777: The binary sequence methods on bytes and bytearray are now
   documented explicitly, rather than assuming users will be able to derive
-  the expected behaviour from the behaviour of the corresponding str methods.
+  the expected behaviour from the behaviour of the corresponding str
+  methods.
 
-- Issue #6916: undocument deprecated asynchat.fifo class.
+- bpo-6916: undocument deprecated asynchat.fifo class.
 
-- Issue #17386: Expanded functionality of the ``Doc/make.bat`` script to make
+- bpo-17386: Expanded functionality of the ``Doc/make.bat`` script to make
   it much more comparable to ``Doc/Makefile``.
 
-- Issue #21312: Update the thread_foobar.h template file to include newer
-  threading APIs.  Patch by Jack McCracken.
+- bpo-21312: Update the thread_foobar.h template file to include newer
+  threading APIs. Patch by Jack McCracken.
 
-- Issue #21043: Remove the recommendation for specific CA organizations and to
+- bpo-21043: Remove the recommendation for specific CA organizations and to
   mention the ability to load the OS certificates.
 
-- Issue #20765: Add missing documentation for PurePath.with_name() and
+- bpo-20765: Add missing documentation for PurePath.with_name() and
   PurePath.with_suffix().
 
-- Issue #19407: New package installation and distribution guides based on
-  the Python Packaging Authority tools. Existing guides have been retained
-  as legacy links from the distutils docs, as they still contain some
-  required reference material for tool developers that isn't recorded
-  anywhere else.
+- bpo-19407: New package installation and distribution guides based on the
+  Python Packaging Authority tools. Existing guides have been retained as
+  legacy links from the distutils docs, as they still contain some required
+  reference material for tool developers that isn't recorded anywhere else.
 
-- Issue #19697: Document cases where __main__.__spec__ is None.
+- bpo-19697: Document cases where __main__.__spec__ is None.
 
 Tests
 -----
 
-- Issue #18982: Add tests for CLI of the calendar module.
+- bpo-18982: Add tests for CLI of the calendar module.
 
-- Issue #19548: Added some additional checks to test_codecs to ensure that
+- bpo-19548: Added some additional checks to test_codecs to ensure that
   statements in the updated documentation remain accurate. Patch by Martin
   Panter.
 
-- Issue #22838: All test_re tests now work with unittest test discovery.
+- bpo-22838: All test_re tests now work with unittest test discovery.
 
-- Issue #22173: Update lib2to3 tests to use unittest test discovery.
+- bpo-22173: Update lib2to3 tests to use unittest test discovery.
 
-- Issue #16000: Convert test_curses to use unittest.
+- bpo-16000: Convert test_curses to use unittest.
 
-- Issue #21456: Skip two tests in test_urllib2net.py if _ssl module not
+- bpo-21456: Skip two tests in test_urllib2net.py if _ssl module not
   present. Patch by Remi Pointel.
 
-- Issue #20746: Fix test_pdb to run in refleak mode (-R).  Patch by Xavier
-  de Gaye.
+- bpo-20746: Fix test_pdb to run in refleak mode (-R).  Patch by Xavier de
+  Gaye.
 
-- Issue #22060: test_ctypes has been somewhat cleaned up and simplified; it
-  now uses unittest test discovery to find its tests.
+- bpo-22060: test_ctypes has been somewhat cleaned up and simplified; it now
+  uses unittest test discovery to find its tests.
 
-- Issue #22104: regrtest.py no longer holds a reference to the suite of tests
+- bpo-22104: regrtest.py no longer holds a reference to the suite of tests
   loaded from test modules that don't define test_main().
 
-- Issue #22111: Assorted cleanups in test_imaplib.  Patch by Milan Oberkirch.
+- bpo-22111: Assorted cleanups in test_imaplib.  Patch by Milan Oberkirch.
 
-- Issue #22002: Added ``load_package_tests`` function to test.support and used
+- bpo-22002: Added ``load_package_tests`` function to test.support and used
   it to implement/augment test discovery in test_asyncio, test_email,
   test_importlib, test_json, and test_tools.
 
-- Issue #21976: Fix test_ssl to accept LibreSSL version strings.  Thanks
-  to William Orr.
+- bpo-21976: Fix test_ssl to accept LibreSSL version strings.  Thanks to
+  William Orr.
 
-- Issue #21918: Converted test_tools from a module to a package containing
+- bpo-21918: Converted test_tools from a module to a package containing
   separate test files for each tested script.
 
-- Issue #9554: Use modern unittest features in test_argparse. Initial patch by
+- bpo-9554: Use modern unittest features in test_argparse. Initial patch by
   Denver Coneybeare and Radu Voicilas.
 
-- Issue #20155: Changed HTTP method names in failing tests in test_httpservers
-  so that packet filtering software (specifically Windows Base Filtering Engine)
-  does not interfere with the transaction semantics expected by the tests.
+- bpo-20155: Changed HTTP method names in failing tests in test_httpservers
+  so that packet filtering software (specifically Windows Base Filtering
+  Engine) does not interfere with the transaction semantics expected by the
+  tests.
 
-- Issue #19493: Refactored the ctypes test package to skip tests explicitly
+- bpo-19493: Refactored the ctypes test package to skip tests explicitly
   rather than silently.
 
-- Issue #18492: All resources are now allowed when tests are not run by
+- bpo-18492: All resources are now allowed when tests are not run by
   regrtest.py.
 
-- Issue #21634: Fix pystone micro-benchmark: use floor division instead of true
-  division to benchmark integers instead of floating point numbers. Set pystone
-  version to 1.2. Patch written by Lennart Regebro.
+- bpo-21634: Fix pystone micro-benchmark: use floor division instead of true
+  division to benchmark integers instead of floating point numbers. Set
+  pystone version to 1.2. Patch written by Lennart Regebro.
 
-- Issue #21605: Added tests for Tkinter images.
+- bpo-21605: Added tests for Tkinter images.
 
-- Issue #21493: Added test for ntpath.expanduser().  Original patch by
-  Claudiu Popa.
+- bpo-21493: Added test for ntpath.expanduser().  Original patch by Claudiu
+  Popa.
 
-- Issue #19925: Added tests for the spwd module. Original patch by Vajrasky Kok.
+- bpo-19925: Added tests for the spwd module. Original patch by Vajrasky
+  Kok.
 
-- Issue #21522: Added Tkinter tests for Listbox.itemconfigure(),
+- bpo-21522: Added Tkinter tests for Listbox.itemconfigure(),
   PanedWindow.paneconfigure(), and Menu.entryconfigure().
 
-- Issue #17756: Fix test_code test when run from the installed location.
+- bpo-17756: Fix test_code test when run from the installed location.
 
-- Issue #17752: Fix distutils tests when run from the installed location.
+- bpo-17752: Fix distutils tests when run from the installed location.
 
-- Issue #18604: Consolidated checks for GUI availability.  All platforms now
-  at least check whether Tk can be instantiated when the GUI resource is
+- bpo-18604: Consolidated checks for GUI availability.  All platforms now at
+  least check whether Tk can be instantiated when the GUI resource is
   requested.
 
-- Issue #21275: Fix a socket test on KFreeBSD.
+- bpo-21275: Fix a socket test on KFreeBSD.
 
-- Issue #21223: Pass test_site/test_startup_imports when some of the extensions
+- bpo-21223: Pass test_site/test_startup_imports when some of the extensions
   are built as builtins.
 
-- Issue #20635: Added tests for Tk geometry managers.
+- bpo-20635: Added tests for Tk geometry managers.
 
 - Add test case for freeze.
 
-- Issue #20743: Fix a reference leak in test_tcl.
+- bpo-20743: Fix a reference leak in test_tcl.
 
-- Issue #21097: Move test_namespace_pkgs into test_importlib.
+- bpo-21097: Move test_namespace_pkgs into test_importlib.
 
-- Issue #21503: Use test_both() consistently in test_importlib.
+- bpo-21503: Use test_both() consistently in test_importlib.
 
-- Issue #20939: Avoid various network test failures due to new
-  redirect of http://www.python.org/ to https://www.python.org:
-  use http://www.example.com instead.
+- bpo-20939: Avoid various network test failures due to new redirect of
+  http://www.python.org/ to https://www.python.org: use
+  http://www.example.com instead.
 
-- Issue #20668: asyncio tests no longer rely on tests.txt file.
-  (Patch by Vajrasky Kok)
+- bpo-20668: asyncio tests no longer rely on tests.txt file. (Patch by
+  Vajrasky Kok)
 
-- Issue #21093: Prevent failures of ctypes test_macholib on OS X if a
-  copy of libz exists in $HOME/lib or /usr/local/lib.
+- bpo-21093: Prevent failures of ctypes test_macholib on OS X if a copy of
+  libz exists in $HOME/lib or /usr/local/lib.
 
-- Issue #22770: Prevent some Tk segfaults on OS X when running gui tests.
+- bpo-22770: Prevent some Tk segfaults on OS X when running gui tests.
 
-- Issue #23211: Workaround test_logging failure on some OS X 10.6 systems.
+- bpo-23211: Workaround test_logging failure on some OS X 10.6 systems.
 
-- Issue #23345: Prevent test_ssl failures with large OpenSSL patch level
-  values (like 0.9.8zc).
+- bpo-23345: Prevent test_ssl failures with large OpenSSL patch level values
+  (like 0.9.8zc).
 
 Tools/Demos
 -----------
 
-- Issue #22314: pydoc now works when the LINES environment variable is set.
+- bpo-22314: pydoc now works when the LINES environment variable is set.
 
-- Issue #22615: Argument Clinic now supports the "type" argument for the
-  int converter.  This permits using the int converter with enums and
-  typedefs.
+- bpo-22615: Argument Clinic now supports the "type" argument for the int
+  converter. This permits using the int converter with enums and typedefs.
 
-- Issue #20076: The makelocalealias.py script no longer ignores UTF-8 mapping.
+- bpo-20076: The makelocalealias.py script no longer ignores UTF-8 mapping.
 
-- Issue #20079: The makelocalealias.py script now can parse the SUPPORTED file
+- bpo-20079: The makelocalealias.py script now can parse the SUPPORTED file
   from glibc sources and supports command line options for source paths.
 
-- Issue #22201: Command-line interface of the zipfile module now correctly
+- bpo-22201: Command-line interface of the zipfile module now correctly
   extracts ZIP files with directory entries.  Patch by Ryan Wilson.
 
-- Issue #22120: For functions using an unsigned integer return converter,
-  Argument Clinic now generates a cast to that type for the comparison
-  to -1 in the generated code.  (This suppresses a compilation warning.)
+- bpo-22120: For functions using an unsigned integer return converter,
+  Argument Clinic now generates a cast to that type for the comparison to -1
+  in the generated code.  (This suppresses a compilation warning.)
 
-- Issue #18974: Tools/scripts/diff.py now uses argparse instead of optparse.
+- bpo-18974: Tools/scripts/diff.py now uses argparse instead of optparse.
 
-- Issue #21906: Make Tools/scripts/md5sum.py work in Python 3.
-  Patch by Zachary Ware.
+- bpo-21906: Make Tools/scripts/md5sum.py work in Python 3. Patch by Zachary
+  Ware.
 
-- Issue #21629: Fix Argument Clinic's "--converters" feature.
+- bpo-21629: Fix Argument Clinic's "--converters" feature.
 
 - Add support for ``yield from`` to 2to3.
 
 - Add support for the PEP 465 matrix multiplication operator to 2to3.
 
-- Issue #16047: Fix module exception list and __file__ handling in freeze.
+- bpo-16047: Fix module exception list and __file__ handling in freeze.
   Patch by Meador Inge.
 
-- Issue #11824: Consider ABI tags in freeze. Patch by Meador Inge.
+- bpo-11824: Consider ABI tags in freeze. Patch by Meador Inge.
 
-- Issue #20535: PYTHONWARNING no longer affects the run_tests.py script.
-  Patch by Arfrever Frehtes Taifersar Arahesis.
+- bpo-20535: PYTHONWARNING no longer affects the run_tests.py script. Patch
+  by Arfrever Frehtes Taifersar Arahesis.
 
 Windows
 -------
 
-- Issue #23260: Update Windows installer
+- bpo-23260: Update Windows installer
 
 - The bundled version of Tcl/Tk has been updated to 8.6.3.  The most visible
   result of this change is the addition of new native file dialogs when
   running on Windows Vista or newer.  See Tcl/Tk's TIP 432 for more
-  information.  Also, this version of Tcl/Tk includes support for Windows 10.
+  information.  Also, this version of Tcl/Tk includes support for Windows
+  10.
 
-- Issue #17896: The Windows build scripts now expect external library sources
+- bpo-17896: The Windows build scripts now expect external library sources
   to be in ``PCbuild\..\externals`` rather than ``PCbuild\..\..``.
 
-- Issue #17717: The Windows build scripts now use a copy of NASM pulled from
+- bpo-17717: The Windows build scripts now use a copy of NASM pulled from
   svn.python.org to build OpenSSL.
 
-- Issue #21907: Improved the batch scripts provided for building Python.
+- bpo-21907: Improved the batch scripts provided for building Python.
 
-- Issue #22644: The bundled version of OpenSSL has been updated to 1.0.1j.
+- bpo-22644: The bundled version of OpenSSL has been updated to 1.0.1j.
 
-- Issue #10747: Use versioned labels in the Windows start menu.
-  Patch by Olive Kilburn.
+- bpo-10747: Use versioned labels in the Windows start menu. Patch by Olive
+  Kilburn.
 
-- Issue #22980: .pyd files with a version and platform tag (for example,
+- bpo-22980: .pyd files with a version and platform tag (for example,
   ".cp35-win32.pyd") will now be loaded in preference to those without tags.
 
 
index c43b112..f16c460 100644 (file)
@@ -1 +1 @@
-This document is moved to https://docs.python.org/devguide/faq.html#how-do-i-port-python-to-a-new-platform
+This document is moved to https://devguide.python.org/porting/
index 385b654..075b974 100644 (file)
@@ -438,7 +438,7 @@ Main website:  https://www.python.org/
 .br
 Documentation:  https://docs.python.org/
 .br
-Developer resources:  https://docs.python.org/devguide/
+Developer resources:  https://devguide.python.org/
 .br
 Downloads:  https://www.python.org/downloads/
 .br
index 8b87fc8..735bacb 100644 (file)
@@ -250,6 +250,7 @@ _symtable symtablemodule.c
 #_sha1 sha1module.c
 #_sha256 sha256module.c
 #_sha512 sha512module.c
+#_sha3 _sha3/sha3module.c
 
 # _blake module
 #_blake2 _blake2/blake2module.c _blake2/blake2b_impl.c _blake2/blake2s_impl.c
index 492b983..35632a6 100644 (file)
@@ -68,7 +68,7 @@ typedef struct {
     PyObject_HEAD
     TaskObj *sw_task;
     PyObject *sw_arg;
-} TaskSendMethWrapper;
+} TaskStepMethWrapper;
 
 typedef struct {
     PyObject_HEAD
@@ -92,11 +92,11 @@ static int
 future_schedule_callbacks(FutureObj *fut)
 {
     Py_ssize_t len;
-    PyObject* iters;
+    PyObject *callbacks;
     int i;
 
     if (fut->fut_callbacks == NULL) {
-        PyErr_SetString(PyExc_RuntimeError, "NULL callbacks");
+        PyErr_SetString(PyExc_RuntimeError, "uninitialized Future object");
         return -1;
     }
 
@@ -105,43 +105,42 @@ future_schedule_callbacks(FutureObj *fut)
         return 0;
     }
 
-    iters = PyList_GetSlice(fut->fut_callbacks, 0, len);
-    if (iters == NULL) {
+    callbacks = PyList_GetSlice(fut->fut_callbacks, 0, len);
+    if (callbacks == NULL) {
         return -1;
     }
     if (PyList_SetSlice(fut->fut_callbacks, 0, len, NULL) < 0) {
-        Py_DECREF(iters);
+        Py_DECREF(callbacks);
         return -1;
     }
 
     for (i = 0; i < len; i++) {
-        PyObject *handle = NULL;
-        PyObject *cb = PyList_GET_ITEM(iters, i);
+        PyObject *handle;
+        PyObject *cb = PyList_GET_ITEM(callbacks, i);
 
-        handle = _PyObject_CallMethodId(
-            fut->fut_loop, &PyId_call_soon, "OO", cb, fut, NULL);
+        handle = _PyObject_CallMethodIdObjArgs(fut->fut_loop, &PyId_call_soon,
+                                               cb, fut, NULL);
 
         if (handle == NULL) {
-            Py_DECREF(iters);
+            Py_DECREF(callbacks);
             return -1;
         }
-        else {
-            Py_DECREF(handle);
-        }
+        Py_DECREF(handle);
     }
 
-    Py_DECREF(iters);
+    Py_DECREF(callbacks);
     return 0;
 }
 
 static int
 future_init(FutureObj *fut, PyObject *loop)
 {
-    PyObject *res = NULL;
+    PyObject *res;
+    int is_true;
     _Py_IDENTIFIER(get_debug);
 
-    if (loop == NULL || loop == Py_None) {
-        loop = PyObject_CallObject(asyncio_get_event_loop, NULL);
+    if (loop == Py_None) {
+        loop = _PyObject_CallNoArg(asyncio_get_event_loop);
         if (loop == NULL) {
             return -1;
         }
@@ -149,25 +148,25 @@ future_init(FutureObj *fut, PyObject *loop)
     else {
         Py_INCREF(loop);
     }
-    Py_CLEAR(fut->fut_loop);
-    fut->fut_loop = loop;
+    Py_XSETREF(fut->fut_loop, loop);
 
     res = _PyObject_CallMethodId(fut->fut_loop, &PyId_get_debug, NULL);
     if (res == NULL) {
         return -1;
     }
-    if (PyObject_IsTrue(res)) {
-        Py_CLEAR(res);
-        fut->fut_source_tb = PyObject_CallObject(traceback_extract_stack, NULL);
+    is_true = PyObject_IsTrue(res);
+    Py_DECREF(res);
+    if (is_true < 0) {
+        return -1;
+    }
+    if (is_true) {
+        Py_XSETREF(fut->fut_source_tb, _PyObject_CallNoArg(traceback_extract_stack));
         if (fut->fut_source_tb == NULL) {
             return -1;
         }
     }
-    else {
-        Py_CLEAR(res);
-    }
 
-    fut->fut_callbacks = PyList_New(0);
+    Py_XSETREF(fut->fut_callbacks, PyList_New(0));
     if (fut->fut_callbacks == NULL) {
         return -1;
     }
@@ -183,6 +182,7 @@ future_set_result(FutureObj *fut, PyObject *res)
         return NULL;
     }
 
+    assert(!fut->fut_result);
     Py_INCREF(res);
     fut->fut_result = res;
     fut->fut_state = STATE_FINISHED;
@@ -208,6 +208,11 @@ future_set_exception(FutureObj *fut, PyObject *exc)
         if (exc_val == NULL) {
             return NULL;
         }
+        if (fut->fut_state != STATE_PENDING) {
+            Py_DECREF(exc_val);
+            PyErr_SetString(asyncio_InvalidStateError, "invalid state");
+            return NULL;
+        }
     }
     else {
         exc_val = exc;
@@ -226,6 +231,7 @@ future_set_exception(FutureObj *fut, PyObject *exc)
         return NULL;
     }
 
+    assert(!fut->fut_exception);
     fut->fut_exception = exc_val;
     fut->fut_state = STATE_FINISHED;
 
@@ -240,31 +246,14 @@ future_set_exception(FutureObj *fut, PyObject *exc)
 static int
 future_get_result(FutureObj *fut, PyObject **result)
 {
-    PyObject *exc;
-
     if (fut->fut_state == STATE_CANCELLED) {
-        exc = _PyObject_CallNoArg(asyncio_CancelledError);
-        if (exc == NULL) {
-            return -1;
-        }
-        *result = exc;
-        return 1;
+        PyErr_SetNone(asyncio_CancelledError);
+        return -1;
     }
 
     if (fut->fut_state != STATE_FINISHED) {
-        PyObject *msg = PyUnicode_FromString("Result is not ready.");
-        if (msg == NULL) {
-            return -1;
-        }
-
-        exc = _PyObject_CallArg1(asyncio_InvalidStateError, msg);
-        Py_DECREF(msg);
-        if (exc == NULL) {
-            return -1;
-        }
-
-        *result = exc;
-        return 1;
+        PyErr_SetString(asyncio_InvalidStateError, "Result is not set.");
+        return -1;
     }
 
     fut->fut_log_tb = 0;
@@ -283,17 +272,19 @@ static PyObject *
 future_add_done_callback(FutureObj *fut, PyObject *arg)
 {
     if (fut->fut_state != STATE_PENDING) {
-        PyObject *handle = _PyObject_CallMethodId(
-            fut->fut_loop, &PyId_call_soon, "OO", arg, fut, NULL);
-
+        PyObject *handle = _PyObject_CallMethodIdObjArgs(fut->fut_loop,
+                                                         &PyId_call_soon,
+                                                         arg, fut, NULL);
         if (handle == NULL) {
             return NULL;
         }
-        else {
-            Py_DECREF(handle);
-        }
+        Py_DECREF(handle);
     }
     else {
+        if (fut->fut_callbacks == NULL) {
+            PyErr_SetString(PyExc_RuntimeError, "uninitialized Future object");
+            return NULL;
+        }
         int err = PyList_Append(fut->fut_callbacks, arg);
         if (err != 0) {
             return NULL;
@@ -323,7 +314,7 @@ future_cancel(FutureObj *fut)
 _asyncio.Future.__init__
 
     *
-    loop: 'O' = NULL
+    loop: object = None
 
 This class is *almost* compatible with concurrent.futures.Future.
 
@@ -341,7 +332,7 @@ This class is *almost* compatible with concurrent.futures.Future.
 
 static int
 _asyncio_Future___init___impl(FutureObj *self, PyObject *loop)
-/*[clinic end generated code: output=9ed75799eaccb5d6 input=8e1681f23605be2d]*/
+/*[clinic end generated code: output=9ed75799eaccb5d6 input=89af317082bc0bf8]*/
 
 {
     return future_init(self, loop);
@@ -419,12 +410,12 @@ _asyncio_Future_exception_impl(FutureObj *self)
 /*[clinic end generated code: output=88b20d4f855e0710 input=733547a70c841c68]*/
 {
     if (self->fut_state == STATE_CANCELLED) {
-        PyErr_SetString(asyncio_CancelledError, "");
+        PyErr_SetNone(asyncio_CancelledError);
         return NULL;
     }
 
     if (self->fut_state != STATE_FINISHED) {
-        PyErr_SetString(asyncio_InvalidStateError, "Result is not ready.");
+        PyErr_SetString(asyncio_InvalidStateError, "Exception is not set.");
         return NULL;
     }
 
@@ -440,7 +431,7 @@ _asyncio_Future_exception_impl(FutureObj *self)
 /*[clinic input]
 _asyncio.Future.set_result
 
-    res: 'O'
+    res: object
     /
 
 Mark the future done and set its result.
@@ -451,7 +442,7 @@ InvalidStateError.
 
 static PyObject *
 _asyncio_Future_set_result(FutureObj *self, PyObject *res)
-/*[clinic end generated code: output=a620abfc2796bfb6 input=8619565e0503357e]*/
+/*[clinic end generated code: output=a620abfc2796bfb6 input=5b9dc180f1baa56d]*/
 {
     return future_set_result(self, res);
 }
@@ -459,7 +450,7 @@ _asyncio_Future_set_result(FutureObj *self, PyObject *res)
 /*[clinic input]
 _asyncio.Future.set_exception
 
-    exception: 'O'
+    exception: object
     /
 
 Mark the future done and set an exception.
@@ -470,7 +461,7 @@ InvalidStateError.
 
 static PyObject *
 _asyncio_Future_set_exception(FutureObj *self, PyObject *exception)
-/*[clinic end generated code: output=f1c1b0cd321be360 input=1377dbe15e6ea186]*/
+/*[clinic end generated code: output=f1c1b0cd321be360 input=e45b7d7aa71cc66d]*/
 {
     return future_set_exception(self, exception);
 }
@@ -478,7 +469,7 @@ _asyncio_Future_set_exception(FutureObj *self, PyObject *exception)
 /*[clinic input]
 _asyncio.Future.add_done_callback
 
-    fn: 'O'
+    fn: object
     /
 
 Add a callback to be run when the future becomes done.
@@ -490,7 +481,7 @@ scheduled with call_soon.
 
 static PyObject *
 _asyncio_Future_add_done_callback(FutureObj *self, PyObject *fn)
-/*[clinic end generated code: output=819e09629b2ec2b5 input=8cce187e32cec6a8]*/
+/*[clinic end generated code: output=819e09629b2ec2b5 input=8f818b39990b027d]*/
 {
     return future_add_done_callback(self, fn);
 }
@@ -498,7 +489,7 @@ _asyncio_Future_add_done_callback(FutureObj *self, PyObject *fn)
 /*[clinic input]
 _asyncio.Future.remove_done_callback
 
-    fn: 'O'
+    fn: object
     /
 
 Remove all instances of a callback from the "call when done" list.
@@ -508,11 +499,16 @@ Returns the number of callbacks removed.
 
 static PyObject *
 _asyncio_Future_remove_done_callback(FutureObj *self, PyObject *fn)
-/*[clinic end generated code: output=5ab1fb52b24ef31f input=3fedb73e1409c31c]*/
+/*[clinic end generated code: output=5ab1fb52b24ef31f input=0a43280a149d505b]*/
 {
     PyObject *newlist;
     Py_ssize_t len, i, j=0;
 
+    if (self->fut_callbacks == NULL) {
+        PyErr_SetString(PyExc_RuntimeError, "uninitialized Future object");
+        return NULL;
+    }
+
     len = PyList_GET_SIZE(self->fut_callbacks);
     if (len == 0) {
         return PyLong_FromSsize_t(0);
@@ -526,22 +522,31 @@ _asyncio_Future_remove_done_callback(FutureObj *self, PyObject *fn)
     for (i = 0; i < PyList_GET_SIZE(self->fut_callbacks); i++) {
         int ret;
         PyObject *item = PyList_GET_ITEM(self->fut_callbacks, i);
-
-        if ((ret = PyObject_RichCompareBool(fn, item, Py_EQ)) < 0) {
-            goto fail;
-        }
+        Py_INCREF(item);
+        ret = PyObject_RichCompareBool(fn, item, Py_EQ);
         if (ret == 0) {
-            Py_INCREF(item);
-            PyList_SET_ITEM(newlist, j, item);
-            j++;
+            if (j < len) {
+                PyList_SET_ITEM(newlist, j, item);
+                j++;
+                continue;
+            }
+            ret = PyList_Append(newlist, item);
+        }
+        Py_DECREF(item);
+        if (ret < 0) {
+            goto fail;
         }
     }
 
-    if (PyList_SetSlice(newlist, j, len, NULL) < 0) {
-        goto fail;
+    if (j < len) {
+        Py_SIZE(newlist) = j;
     }
-    if (PyList_SetSlice(self->fut_callbacks, 0, len, newlist) < 0) {
-        goto fail;
+    j = PyList_GET_SIZE(newlist);
+    len = PyList_GET_SIZE(self->fut_callbacks);
+    if (j != len) {
+        if (PyList_SetSlice(self->fut_callbacks, 0, len, newlist) < 0) {
+            goto fail;
+        }
     }
     Py_DECREF(newlist);
     return PyLong_FromSsize_t(len - j);
@@ -722,7 +727,7 @@ FutureObj_get_state(FutureObj *fut)
     default:
         assert (0);
     }
-    Py_INCREF(ret);
+    Py_XINCREF(ret);
     return ret;
 }
 
@@ -758,25 +763,14 @@ FutureObj_repr(FutureObj *fut)
 {
     _Py_IDENTIFIER(_repr_info);
 
-    PyObject *_repr_info = _PyUnicode_FromId(&PyId__repr_info);  // borrowed
-    if (_repr_info == NULL) {
-        return NULL;
-    }
-
-    PyObject *rinfo = PyObject_CallMethodObjArgs((PyObject*)fut, _repr_info,
-                                                 NULL);
+    PyObject *rinfo = _PyObject_CallMethodIdObjArgs((PyObject*)fut,
+                                                    &PyId__repr_info,
+                                                    NULL);
     if (rinfo == NULL) {
         return NULL;
     }
 
-    PyObject *sp = PyUnicode_FromString(" ");
-    if (sp == NULL) {
-        Py_DECREF(rinfo);
-        return NULL;
-    }
-
-    PyObject *rinfo_s = PyUnicode_Join(sp, rinfo);
-    Py_DECREF(sp);
+    PyObject *rinfo_s = PyUnicode_Join(NULL, rinfo);
     Py_DECREF(rinfo);
     if (rinfo_s == NULL) {
         return NULL;
@@ -786,7 +780,7 @@ FutureObj_repr(FutureObj *fut)
     PyObject *type_name = PyObject_GetAttrString((PyObject*)Py_TYPE(fut),
                                                  "__name__");
     if (type_name != NULL) {
-        rstr = PyUnicode_FromFormat("<%S %S>", type_name, rinfo_s);
+        rstr = PyUnicode_FromFormat("<%S %U>", type_name, rinfo_s);
         Py_DECREF(type_name);
     }
     Py_DECREF(rinfo_s);
@@ -802,22 +796,21 @@ FutureObj_finalize(FutureObj *fut)
     _Py_IDENTIFIER(future);
     _Py_IDENTIFIER(source_traceback);
 
+    PyObject *error_type, *error_value, *error_traceback;
+    PyObject *context;
+    PyObject *type_name;
+    PyObject *message = NULL;
+    PyObject *func;
+
     if (!fut->fut_log_tb) {
         return;
     }
     assert(fut->fut_exception != NULL);
-    fut->fut_log_tb = 0;;
+    fut->fut_log_tb = 0;
 
-    PyObject *error_type, *error_value, *error_traceback;
     /* Save the current exception, if any. */
     PyErr_Fetch(&error_type, &error_value, &error_traceback);
 
-    PyObject *context = NULL;
-    PyObject *type_name = NULL;
-    PyObject *message = NULL;
-    PyObject *func = NULL;
-    PyObject *res = NULL;
-
     context = PyDict_New();
     if (context == NULL) {
         goto finally;
@@ -830,6 +823,7 @@ FutureObj_finalize(FutureObj *fut)
 
     message = PyUnicode_FromFormat(
         "%S exception was never retrieved", type_name);
+    Py_DECREF(type_name);
     if (message == NULL) {
         goto finally;
     }
@@ -848,18 +842,19 @@ FutureObj_finalize(FutureObj *fut)
 
     func = _PyObject_GetAttrId(fut->fut_loop, &PyId_call_exception_handler);
     if (func != NULL) {
-        res = _PyObject_CallArg1(func, context);
+        PyObject *res = _PyObject_CallArg1(func, context);
         if (res == NULL) {
             PyErr_WriteUnraisable(func);
         }
+        else {
+            Py_DECREF(res);
+        }
+        Py_DECREF(func);
     }
 
 finally:
-    Py_CLEAR(context);
-    Py_CLEAR(type_name);
-    Py_CLEAR(message);
-    Py_CLEAR(func);
-    Py_CLEAR(res);
+    Py_XDECREF(context);
+    Py_XDECREF(message);
 
     /* Restore the saved exception. */
     PyErr_Restore(error_type, error_value, error_traceback);
@@ -964,6 +959,8 @@ FutureObj_dealloc(PyObject *self)
         }
     }
 
+    PyObject_GC_UnTrack(self);
+
     if (fut->fut_weakreflist != NULL) {
         PyObject_ClearWeakRefs(self);
     }
@@ -1004,22 +1001,19 @@ FutureIter_iternext(futureiterobject *it)
             Py_INCREF(fut);
             return (PyObject *)fut;
         }
-        PyErr_Format(PyExc_AssertionError,
-                     "yield from wasn't used with future");
+        PyErr_SetString(PyExc_AssertionError,
+                        "yield from wasn't used with future");
         return NULL;
     }
 
+    it->future = NULL;
     res = _asyncio_Future_result_impl(fut);
     if (res != NULL) {
         /* The result of the Future is not an exception. */
-        if (_PyGen_SetStopIterationValue(res) < 0) {
-            Py_DECREF(res);
-            return NULL;
-        }
+        (void)_PyGen_SetStopIterationValue(res);
         Py_DECREF(res);
     }
 
-    it->future = NULL;
     Py_DECREF(fut);
     return NULL;
 }
@@ -1036,7 +1030,7 @@ FutureIter_send(futureiterobject *self, PyObject *unused)
 static PyObject *
 FutureIter_throw(futureiterobject *self, PyObject *args)
 {
-    PyObject *type=NULL, *val=NULL, *tb=NULL;
+    PyObject *type, *val = NULL, *tb = NULL;
     if (!PyArg_ParseTuple(args, "O|OO", &type, &val, &tb))
         return NULL;
 
@@ -1080,7 +1074,7 @@ FutureIter_throw(futureiterobject *self, PyObject *args)
 
     PyErr_Restore(type, val, tb);
 
-    return FutureIter_iternext(self);
+    return NULL;
 
   fail:
     Py_DECREF(type);
@@ -1161,7 +1155,7 @@ static PyObject * task_step(TaskObj *, PyObject *);
 /* ----- Task._step wrapper */
 
 static int
-TaskSendMethWrapper_clear(TaskSendMethWrapper *o)
+TaskStepMethWrapper_clear(TaskStepMethWrapper *o)
 {
     Py_CLEAR(o->sw_task);
     Py_CLEAR(o->sw_arg);
@@ -1169,22 +1163,30 @@ TaskSendMethWrapper_clear(TaskSendMethWrapper *o)
 }
 
 static void
-TaskSendMethWrapper_dealloc(TaskSendMethWrapper *o)
+TaskStepMethWrapper_dealloc(TaskStepMethWrapper *o)
 {
     PyObject_GC_UnTrack(o);
-    (void)TaskSendMethWrapper_clear(o);
+    (void)TaskStepMethWrapper_clear(o);
     Py_TYPE(o)->tp_free(o);
 }
 
 static PyObject *
-TaskSendMethWrapper_call(TaskSendMethWrapper *o,
+TaskStepMethWrapper_call(TaskStepMethWrapper *o,
                          PyObject *args, PyObject *kwds)
 {
+    if (kwds != NULL && PyDict_Size(kwds) != 0) {
+        PyErr_SetString(PyExc_TypeError, "function takes no keyword arguments");
+        return NULL;
+    }
+    if (args != NULL && PyTuple_GET_SIZE(args) != 0) {
+        PyErr_SetString(PyExc_TypeError, "function takes no positional arguments");
+        return NULL;
+    }
     return task_call_step(o->sw_task, o->sw_arg);
 }
 
 static int
-TaskSendMethWrapper_traverse(TaskSendMethWrapper *o,
+TaskStepMethWrapper_traverse(TaskStepMethWrapper *o,
                              visitproc visit, void *arg)
 {
     Py_VISIT(o->sw_task);
@@ -1193,7 +1195,7 @@ TaskSendMethWrapper_traverse(TaskSendMethWrapper *o,
 }
 
 static PyObject *
-TaskSendMethWrapper_get___self__(TaskSendMethWrapper *o)
+TaskStepMethWrapper_get___self__(TaskStepMethWrapper *o)
 {
     if (o->sw_task) {
         Py_INCREF(o->sw_task);
@@ -1202,30 +1204,30 @@ TaskSendMethWrapper_get___self__(TaskSendMethWrapper *o)
     Py_RETURN_NONE;
 }
 
-static PyGetSetDef TaskSendMethWrapper_getsetlist[] = {
-    {"__self__", (getter)TaskSendMethWrapper_get___self__, NULL, NULL},
+static PyGetSetDef TaskStepMethWrapper_getsetlist[] = {
+    {"__self__", (getter)TaskStepMethWrapper_get___self__, NULL, NULL},
     {NULL} /* Sentinel */
 };
 
-PyTypeObject TaskSendMethWrapper_Type = {
+PyTypeObject TaskStepMethWrapper_Type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "TaskSendMethWrapper",
-    .tp_basicsize = sizeof(TaskSendMethWrapper),
+    "TaskStepMethWrapper",
+    .tp_basicsize = sizeof(TaskStepMethWrapper),
     .tp_itemsize = 0,
-    .tp_getset = TaskSendMethWrapper_getsetlist,
-    .tp_dealloc = (destructor)TaskSendMethWrapper_dealloc,
-    .tp_call = (ternaryfunc)TaskSendMethWrapper_call,
+    .tp_getset = TaskStepMethWrapper_getsetlist,
+    .tp_dealloc = (destructor)TaskStepMethWrapper_dealloc,
+    .tp_call = (ternaryfunc)TaskStepMethWrapper_call,
     .tp_getattro = PyObject_GenericGetAttr,
     .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
-    .tp_traverse = (traverseproc)TaskSendMethWrapper_traverse,
-    .tp_clear = (inquiry)TaskSendMethWrapper_clear,
+    .tp_traverse = (traverseproc)TaskStepMethWrapper_traverse,
+    .tp_clear = (inquiry)TaskStepMethWrapper_clear,
 };
 
 static PyObject *
-TaskSendMethWrapper_new(TaskObj *task, PyObject *arg)
+TaskStepMethWrapper_new(TaskObj *task, PyObject *arg)
 {
-    TaskSendMethWrapper *o;
-    o = PyObject_GC_New(TaskSendMethWrapper, &TaskSendMethWrapper_Type);
+    TaskStepMethWrapper *o;
+    o = PyObject_GC_New(TaskStepMethWrapper, &TaskStepMethWrapper_Type);
     if (o == NULL) {
         return NULL;
     }
@@ -1248,7 +1250,11 @@ TaskWakeupMethWrapper_call(TaskWakeupMethWrapper *o,
 {
     PyObject *fut;
 
-    if (!PyArg_ParseTuple(args, "O|", &fut)) {
+    if (kwds != NULL && PyDict_Size(kwds) != 0) {
+        PyErr_SetString(PyExc_TypeError, "function takes no keyword arguments");
+        return NULL;
+    }
+    if (!PyArg_ParseTuple(args, "O", &fut)) {
         return NULL;
     }
 
@@ -1312,16 +1318,16 @@ TaskWakeupMethWrapper_new(TaskObj *task)
 /*[clinic input]
 _asyncio.Task.__init__
 
-    coro: 'O'
+    coro: object
     *
-    loop: 'O' = NULL
+    loop: object = None
 
 A coroutine wrapped in a Future.
 [clinic start generated code]*/
 
 static int
 _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop)
-/*[clinic end generated code: output=9f24774c2287fc2f input=71d8d28c201a18cd]*/
+/*[clinic end generated code: output=9f24774c2287fc2f input=8d132974b049593e]*/
 {
     PyObject *res;
     _Py_IDENTIFIER(add);
@@ -1427,7 +1433,7 @@ TaskObj_get_fut_waiter(TaskObj *task)
 @classmethod
 _asyncio.Task.current_task
 
-    loop: 'O' = None
+    loop: object = None
 
 Return the currently running task in an event loop or None.
 
@@ -1438,7 +1444,7 @@ None is returned when called not in the context of a Task.
 
 static PyObject *
 _asyncio_Task_current_task_impl(PyTypeObject *type, PyObject *loop)
-/*[clinic end generated code: output=99fbe7332c516e03 input=a0d6cdf2e3b243e1]*/
+/*[clinic end generated code: output=99fbe7332c516e03 input=cd14770c5b79c7eb]*/
 {
     PyObject *res;
 
@@ -1500,12 +1506,14 @@ task_all_tasks(PyObject *loop)
         Py_DECREF(task_loop);
         Py_DECREF(task);
     }
-
+    if (PyErr_Occurred()) {
+        goto fail;
+    }
     Py_DECREF(iter);
     return set;
 
 fail:
-    Py_XDECREF(set);
+    Py_DECREF(set);
     Py_XDECREF(iter);
     return NULL;
 }
@@ -1514,7 +1522,7 @@ fail:
 @classmethod
 _asyncio.Task.all_tasks
 
-    loop: 'O' = None
+    loop: object = None
 
 Return a set of all tasks for an event loop.
 
@@ -1523,7 +1531,7 @@ By default all tasks for the current event loop are returned.
 
 static PyObject *
 _asyncio_Task_all_tasks_impl(PyTypeObject *type, PyObject *loop)
-/*[clinic end generated code: output=11f9b20749ccca5d input=c6f5b53bd487488f]*/
+/*[clinic end generated code: output=11f9b20749ccca5d input=497f80bc9ce726b5]*/
 {
     PyObject *res;
 
@@ -1617,7 +1625,7 @@ _asyncio_Task_cancel_impl(TaskObj *self)
 _asyncio.Task.get_stack
 
     *
-    limit: 'O' = None
+    limit: object = None
 
 Return the list of stack frames for this task's coroutine.
 
@@ -1642,7 +1650,7 @@ returned for a suspended coroutine.
 
 static PyObject *
 _asyncio_Task_get_stack_impl(TaskObj *self, PyObject *limit)
-/*[clinic end generated code: output=c9aeeeebd1e18118 input=b1920230a766d17a]*/
+/*[clinic end generated code: output=c9aeeeebd1e18118 input=05b323d42b809b90]*/
 {
     return PyObject_CallFunctionObjArgs(
         asyncio_task_get_stack_func, self, limit, NULL);
@@ -1652,8 +1660,8 @@ _asyncio_Task_get_stack_impl(TaskObj *self, PyObject *limit)
 _asyncio.Task.print_stack
 
     *
-    limit: 'O' = None
-    file: 'O' = None
+    limit: object = None
+    file: object = None
 
 Print the stack or traceback for this task's coroutine.
 
@@ -1667,7 +1675,7 @@ to sys.stderr.
 static PyObject *
 _asyncio_Task_print_stack_impl(TaskObj *self, PyObject *limit,
                                PyObject *file)
-/*[clinic end generated code: output=7339e10314cd3f4d input=19f1e99ab5400bc3]*/
+/*[clinic end generated code: output=7339e10314cd3f4d input=1a0352913b7fcd92]*/
 {
     return PyObject_CallFunctionObjArgs(
         asyncio_task_print_stack_func, self, limit, file, NULL);
@@ -1676,12 +1684,12 @@ _asyncio_Task_print_stack_impl(TaskObj *self, PyObject *limit,
 /*[clinic input]
 _asyncio.Task._step
 
-    exc: 'O' = NULL
+    exc: object = None
 [clinic start generated code]*/
 
 static PyObject *
 _asyncio_Task__step_impl(TaskObj *self, PyObject *exc)
-/*[clinic end generated code: output=7ed23f0cefd5ae42 input=ada4b2324e5370af]*/
+/*[clinic end generated code: output=7ed23f0cefd5ae42 input=1e19a985ace87ca4]*/
 {
     return task_step(self, exc == Py_None ? NULL : exc);
 }
@@ -1689,12 +1697,12 @@ _asyncio_Task__step_impl(TaskObj *self, PyObject *exc)
 /*[clinic input]
 _asyncio.Task._wakeup
 
-    fut: 'O'
+    fut: object
 [clinic start generated code]*/
 
 static PyObject *
 _asyncio_Task__wakeup_impl(TaskObj *self, PyObject *fut)
-/*[clinic end generated code: output=75cb341c760fd071 input=11ee4918a5bdbf21]*/
+/*[clinic end generated code: output=75cb341c760fd071 input=6a0616406f829a7b]*/
 {
     return task_wakeup(self, fut);
 }
@@ -1707,11 +1715,9 @@ TaskObj_finalize(TaskObj *task)
     _Py_IDENTIFIER(message);
     _Py_IDENTIFIER(source_traceback);
 
+    PyObject *context;
     PyObject *message = NULL;
-    PyObject *context = NULL;
-    PyObject *func = NULL;
-    PyObject *res = NULL;
-
+    PyObject *func;
     PyObject *error_type, *error_value, *error_traceback;
 
     if (task->task_state != STATE_PENDING || !task->task_log_destroy_pending) {
@@ -1747,17 +1753,19 @@ TaskObj_finalize(TaskObj *task)
 
     func = _PyObject_GetAttrId(task->task_loop, &PyId_call_exception_handler);
     if (func != NULL) {
-        res = _PyObject_CallArg1(func, context);
+        PyObject *res = _PyObject_CallArg1(func, context);
         if (res == NULL) {
             PyErr_WriteUnraisable(func);
         }
+        else {
+            Py_DECREF(res);
+        }
+        Py_DECREF(func);
     }
 
 finally:
-    Py_CLEAR(context);
-    Py_CLEAR(message);
-    Py_CLEAR(func);
-    Py_CLEAR(res);
+    Py_XDECREF(context);
+    Py_XDECREF(message);
 
     /* Restore the saved exception. */
     PyErr_Restore(error_type, error_value, error_traceback);
@@ -1838,6 +1846,8 @@ TaskObj_dealloc(PyObject *self)
         }
     }
 
+    PyObject_GC_UnTrack(self);
+
     if (task->task_weakreflist != NULL) {
         PyObject_ClearWeakRefs(self);
     }
@@ -1867,9 +1877,6 @@ task_call_step(TaskObj *task, PyObject *arg)
     }
     else {
         /* `task` is a subclass of Task */
-        if (arg == NULL) {
-            arg = Py_None;
-        }
         return _PyObject_CallMethodIdObjArgs((PyObject*)task, &PyId__step,
                                              arg, NULL);
     }
@@ -1880,7 +1887,7 @@ task_call_step_soon(TaskObj *task, PyObject *arg)
 {
     PyObject *handle;
 
-    PyObject *cb = TaskSendMethWrapper_new(task, arg);
+    PyObject *cb = TaskStepMethWrapper_new(task, arg);
     if (cb == NULL) {
         return -1;
     }
@@ -1935,7 +1942,7 @@ task_step_impl(TaskObj *task, PyObject *exc)
     int res;
     int clear_exc = 0;
     PyObject *result = NULL;
-    PyObject *coro = task->task_coro;
+    PyObject *coro;
     PyObject *o;
 
     if (task->task_state != STATE_PENDING) {
@@ -1976,6 +1983,12 @@ task_step_impl(TaskObj *task, PyObject *exc)
 
     Py_CLEAR(task->task_fut_waiter);
 
+    coro = task->task_coro;
+    if (coro == NULL) {
+        PyErr_SetString(PyExc_RuntimeError, "uninitialized Task object");
+        return NULL;
+    }
+
     if (exc == NULL) {
         if (PyGen_CheckExact(coro) || PyCoro_CheckExact(coro)) {
             result = _PyGen_Send((PyGenObject*)coro, Py_None);
@@ -1990,7 +2003,7 @@ task_step_impl(TaskObj *task, PyObject *exc)
             coro, &PyId_throw, exc, NULL);
         if (clear_exc) {
             /* We created 'exc' during this call */
-            Py_CLEAR(exc);
+            Py_DECREF(exc);
         }
     }
 
@@ -2039,13 +2052,13 @@ set_exception:
         o = future_set_exception((FutureObj*)task, ev);
         if (!o) {
             /* An exception in Task.set_exception() */
-            Py_XDECREF(et);
+            Py_DECREF(et);
             Py_XDECREF(tb);
             Py_XDECREF(ev);
             goto fail;
         }
         assert(o == Py_None);
-        Py_CLEAR(o);
+        Py_DECREF(o);
 
         if (!PyErr_GivenExceptionMatches(et, PyExc_Exception)) {
             /* We've got a BaseException; re-raise it */
@@ -2053,7 +2066,7 @@ set_exception:
             goto fail;
         }
 
-        Py_XDECREF(et);
+        Py_DECREF(et);
         Py_XDECREF(tb);
         Py_XDECREF(ev);
 
@@ -2125,7 +2138,7 @@ set_exception:
     }
     else {
         if (o == Py_None) {
-            Py_CLEAR(o);
+            Py_DECREF(o);
         }
         else {
             /* `result` is a Future-compatible object */
@@ -2133,7 +2146,7 @@ set_exception:
             PyObject *res;
 
             int blocking = PyObject_IsTrue(o);
-            Py_CLEAR(o);
+            Py_DECREF(o);
             if (blocking < 0) {
                 goto fail;
             }
@@ -2216,7 +2229,7 @@ set_exception:
         goto fail;
     }
     res = PyObject_IsTrue(o);
-    Py_CLEAR(o);
+    Py_DECREF(o);
     if (res == -1) {
         /* An exception while checking if 'val' is True */
         goto fail;
@@ -2284,14 +2297,8 @@ task_step(TaskObj *task, PyObject *exc)
         PyObject *et, *ev, *tb;
         PyErr_Fetch(&et, &ev, &tb);
         ot = _PyDict_Pop(current_tasks, task->task_loop, NULL);
-        if (ot == NULL) {
-            Py_XDECREF(et);
-            Py_XDECREF(tb);
-            Py_XDECREF(ev);
-            return NULL;
-        }
-        Py_DECREF(ot);
-        PyErr_Restore(et, ev, tb);
+        Py_XDECREF(ot);
+        _PyErr_ChainExceptions(et, ev, tb);
         return NULL;
     }
     else {
@@ -2310,17 +2317,18 @@ task_step(TaskObj *task, PyObject *exc)
 static PyObject *
 task_wakeup(TaskObj *task, PyObject *o)
 {
+    PyObject *et, *ev, *tb;
+    PyObject *result;
     assert(o);
 
     if (Future_CheckExact(o) || Task_CheckExact(o)) {
         PyObject *fut_result = NULL;
         int res = future_get_result((FutureObj*)o, &fut_result);
-        PyObject *result;
 
         switch(res) {
         case -1:
             assert(fut_result == NULL);
-            return NULL;
+            break; /* exception raised */
         case 0:
             Py_DECREF(fut_result);
             return task_call_step(task, NULL);
@@ -2331,29 +2339,32 @@ task_wakeup(TaskObj *task, PyObject *o)
             return result;
         }
     }
-
-    PyObject *fut_result = PyObject_CallMethod(o, "result", NULL);
-    if (fut_result == NULL) {
-        PyObject *et, *ev, *tb;
-        PyObject *res;
-
-        PyErr_Fetch(&et, &ev, &tb);
-        if (!ev || !PyObject_TypeCheck(ev, (PyTypeObject *) et)) {
-            PyErr_NormalizeException(&et, &ev, &tb);
+    else {
+        PyObject *fut_result = PyObject_CallMethod(o, "result", NULL);
+        if (fut_result != NULL) {
+            Py_DECREF(fut_result);
+            return task_call_step(task, NULL);
         }
+        /* exception raised */
+    }
 
-        res = task_call_step(task, ev);
-
-        Py_XDECREF(et);
-        Py_XDECREF(tb);
-        Py_XDECREF(ev);
-
-        return res;
+    PyErr_Fetch(&et, &ev, &tb);
+    if (!PyErr_GivenExceptionMatches(et, PyExc_Exception)) {
+        /* We've got a BaseException; re-raise it */
+        PyErr_Restore(et, ev, tb);
+        return NULL;
     }
-    else {
-        Py_DECREF(fut_result);
-        return task_call_step(task, NULL);
+    if (!ev || !PyObject_TypeCheck(ev, (PyTypeObject *) et)) {
+        PyErr_NormalizeException(&et, &ev, &tb);
     }
+
+    result = task_call_step(task, ev);
+
+    Py_DECREF(et);
+    Py_XDECREF(tb);
+    Py_XDECREF(ev);
+
+    return result;
 }
 
 
@@ -2386,7 +2397,7 @@ module_init(void)
     Py_CLEAR(module); \
     module = PyImport_ImportModule(NAME); \
     if (module == NULL) { \
-        return -1; \
+        goto fail; \
     }
 
 #define GET_MOD_ATTR(VAR, NAME) \
@@ -2416,8 +2427,8 @@ module_init(void)
 
     WITH_MOD("weakref")
     GET_MOD_ATTR(cls, "WeakSet")
-    all_tasks = PyObject_CallObject(cls, NULL);
-    Py_CLEAR(cls);
+    all_tasks = _PyObject_CallNoArg(cls);
+    Py_DECREF(cls);
     if (all_tasks == NULL) {
         goto fail;
     }
@@ -2427,7 +2438,7 @@ module_init(void)
         goto fail;
     }
 
-    Py_CLEAR(module);
+    Py_DECREF(module);
     return 0;
 
 fail:
@@ -2466,7 +2477,7 @@ PyInit__asyncio(void)
     if (PyType_Ready(&FutureIterType) < 0) {
         return NULL;
     }
-    if (PyType_Ready(&TaskSendMethWrapper_Type) < 0) {
+    if (PyType_Ready(&TaskStepMethWrapper_Type) < 0) {
         return NULL;
     }
     if(PyType_Ready(&TaskWakeupMethWrapper_Type) < 0) {
index 3015770..e7a24f3 100644 (file)
@@ -1706,6 +1706,8 @@ dequeiter_traverse(dequeiterobject *dio, visitproc visit, void *arg)
 static void
 dequeiter_dealloc(dequeiterobject *dio)
 {
+    /* bpo-31095: UnTrack is needed before calling any callbacks */
+    PyObject_GC_UnTrack(dio);
     Py_XDECREF(dio->deque);
     PyObject_GC_Del(dio);
 }
@@ -2086,6 +2088,8 @@ static PyMemberDef defdict_members[] = {
 static void
 defdict_dealloc(defdictobject *dd)
 {
+    /* bpo-31095: UnTrack is needed before calling any callbacks */
+    PyObject_GC_UnTrack(dd);
     Py_CLEAR(dd->default_factory);
     PyDict_Type.tp_dealloc((PyObject *)dd);
 }
index 12234e2..1ced630 100644 (file)
@@ -250,6 +250,71 @@ PyDict_GetItemProxy(PyObject *dict, PyObject *key)
 }
 
 /******************************************************************/
+
+/*
+  Allocate a memory block for a pep3118 format string, filled with
+  a suitable PEP 3118 type code corresponding to the given ctypes
+  type. Returns NULL on failure, with the error indicator set.
+
+  This produces type codes in the standard size mode (cf. struct module),
+  since the endianness may need to be swapped to a non-native one
+  later on.
+ */
+static char *
+_ctypes_alloc_format_string_for_type(char code, int big_endian)
+{
+    char *result;
+    char pep_code = '\0';
+
+    switch (code) {
+#if SIZEOF_INT == 2
+    case 'i': pep_code = 'h'; break;
+    case 'I': pep_code = 'H'; break;
+#elif SIZEOF_INT == 4
+    case 'i': pep_code = 'i'; break;
+    case 'I': pep_code = 'I'; break;
+#elif SIZEOF_INT == 8
+    case 'i': pep_code = 'q'; break;
+    case 'I': pep_code = 'Q'; break;
+#else
+# error SIZEOF_INT has an unexpected value
+#endif /* SIZEOF_INT */
+#if SIZEOF_LONG == 4
+    case 'l': pep_code = 'l'; break;
+    case 'L': pep_code = 'L'; break;
+#elif SIZEOF_LONG == 8
+    case 'l': pep_code = 'q'; break;
+    case 'L': pep_code = 'Q'; break;
+#else
+# error SIZEOF_LONG has an unexpected value
+#endif /* SIZEOF_LONG */
+#if SIZEOF__BOOL == 1
+    case '?': pep_code = '?'; break;
+#elif SIZEOF__BOOL == 2
+    case '?': pep_code = 'H'; break;
+#elif SIZEOF__BOOL == 4
+    case '?': pep_code = 'L'; break;
+#elif SIZEOF__BOOL == 8
+    case '?': pep_code = 'Q'; break;
+#else
+# error SIZEOF__BOOL has an unexpected value
+#endif /* SIZEOF__BOOL */
+    default:
+        /* The standard-size code is the same as the ctypes one */
+        pep_code = code;
+        break;
+    }
+
+    result = PyMem_Malloc(3);
+    if (result == NULL)
+        return NULL;
+
+    result[0] = big_endian ? '>' : '<';
+    result[1] = pep_code;
+    result[2] = '\0';
+    return result;
+}
+
 /*
   Allocate a memory block for a pep3118 format string, copy prefix (if
   non-null) and suffix into it.  Returns NULL on failure, with the error
@@ -1926,9 +1991,9 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
     stgdict->setfunc = fmt->setfunc;
     stgdict->getfunc = fmt->getfunc;
 #ifdef WORDS_BIGENDIAN
-    stgdict->format = _ctypes_alloc_format_string(">", proto_str);
+    stgdict->format = _ctypes_alloc_format_string_for_type(proto_str[0], 1);
 #else
-    stgdict->format = _ctypes_alloc_format_string("<", proto_str);
+    stgdict->format = _ctypes_alloc_format_string_for_type(proto_str[0], 0);
 #endif
     if (stgdict->format == NULL) {
         Py_DECREF(result);
@@ -3610,7 +3675,7 @@ _build_callargs(PyCFuncPtrObject *self, PyObject *argtypes,
         case (PARAMFLAG_FIN | PARAMFLAG_FOUT):
             *pinoutmask |= (1 << i); /* mark as inout arg */
             (*pnumretvals)++;
-            /* fall through to PARAMFLAG_FIN... */
+            /* fall through */
         case 0:
         case PARAMFLAG_FIN:
             /* 'in' parameter.  Copy it from inargs. */
index 7d542fb..ff95dff 100644 (file)
@@ -668,7 +668,7 @@ static int ConvParam(PyObject *obj, Py_ssize_t index, struct argument *pa)
 #ifdef CTYPES_UNICODE
     if (PyUnicode_Check(obj)) {
         pa->ffi_type = &ffi_type_pointer;
-        pa->value.p = PyUnicode_AsWideCharString(obj, NULL);
+        pa->value.p = _PyUnicode_AsWideCharString(obj);
         if (pa->value.p == NULL)
             return -1;
         pa->keep = PyCapsule_New(pa->value.p, CTYPES_CAPSULE_NAME_PYMEM, pymem_destructor);
@@ -1231,14 +1231,15 @@ The handle may be used to locate exported functions in this\n\
 module.\n";
 static PyObject *load_library(PyObject *self, PyObject *args)
 {
-    WCHAR *name;
+    const WCHAR *name;
     PyObject *nameobj;
     PyObject *ignored;
     HMODULE hMod;
-    if (!PyArg_ParseTuple(args, "O|O:LoadLibrary", &nameobj, &ignored))
+
+    if (!PyArg_ParseTuple(args, "U|O:LoadLibrary", &nameobj, &ignored))
         return NULL;
 
-    name = PyUnicode_AsUnicode(nameobj);
+    name = _PyUnicode_AsUnicode(nameobj);
     if (!name)
         return NULL;
 
index a43585f..113a815 100644 (file)
@@ -1372,7 +1372,7 @@ Z_set(void *ptr, PyObject *value, Py_ssize_t size)
 
     /* We must create a wchar_t* buffer from the unicode object,
        and keep it alive */
-    buffer = PyUnicode_AsWideCharString(value, NULL);
+    buffer = _PyUnicode_AsWideCharString(value);
     if (!buffer)
         return NULL;
     keep = PyCapsule_New(buffer, CTYPES_CFIELD_CAPSULE_NAME_PYMEM, pymem_destructor);
index d88d06e..7a70951 100644 (file)
@@ -342,10 +342,11 @@ static int
 PyCurses_ConvertToString(PyCursesWindowObject *win, PyObject *obj,
                          PyObject **bytes, wchar_t **wstr)
 {
+    char *str;
     if (PyUnicode_Check(obj)) {
 #ifdef HAVE_NCURSESW
         assert (wstr != NULL);
-        *wstr = PyUnicode_AsWideCharString(obj, NULL);
+        *wstr = _PyUnicode_AsWideCharString(obj);
         if (*wstr == NULL)
             return 0;
         return 2;
@@ -354,12 +355,20 @@ PyCurses_ConvertToString(PyCursesWindowObject *win, PyObject *obj,
         *bytes = PyUnicode_AsEncodedString(obj, win->encoding, NULL);
         if (*bytes == NULL)
             return 0;
+        /* check for embedded null bytes */
+        if (PyBytes_AsStringAndSize(*bytes, &str, NULL) < 0) {
+            return 0;
+        }
         return 1;
 #endif
     }
     else if (PyBytes_Check(obj)) {
         Py_INCREF(obj);
         *bytes = obj;
+        /* check for embedded null bytes */
+        if (PyBytes_AsStringAndSize(*bytes, &str, NULL) < 0) {
+            return 0;
+        }
         return 1;
     }
 
index fcc1f15..13418c9 100644 (file)
@@ -2163,13 +2163,17 @@ dec_from_long(PyTypeObject *type, const PyObject *v,
 /* Return a new PyDecObject from a PyLongObject. Use the context for
    conversion. */
 static PyObject *
-PyDecType_FromLong(PyTypeObject *type, const PyObject *pylong,
-                   PyObject *context)
+PyDecType_FromLong(PyTypeObject *type, const PyObject *v, PyObject *context)
 {
     PyObject *dec;
     uint32_t status = 0;
 
-    dec = dec_from_long(type, pylong, CTX(context), &status);
+    if (!PyLong_Check(v)) {
+        PyErr_SetString(PyExc_TypeError, "argument must be an integer");
+        return NULL;
+    }
+
+    dec = dec_from_long(type, v, CTX(context), &status);
     if (dec == NULL) {
         return NULL;
     }
@@ -2185,15 +2189,20 @@ PyDecType_FromLong(PyTypeObject *type, const PyObject *pylong,
 /* Return a new PyDecObject from a PyLongObject. Use a maximum context
    for conversion. If the conversion is not exact, set InvalidOperation. */
 static PyObject *
-PyDecType_FromLongExact(PyTypeObject *type, const PyObject *pylong,
+PyDecType_FromLongExact(PyTypeObject *type, const PyObject *v,
                         PyObject *context)
 {
     PyObject *dec;
     uint32_t status = 0;
     mpd_context_t maxctx;
 
+    if (!PyLong_Check(v)) {
+        PyErr_SetString(PyExc_TypeError, "argument must be an integer");
+        return NULL;
+    }
+
     mpd_maxcontext(&maxctx);
-    dec = dec_from_long(type, pylong, &maxctx, &status);
+    dec = dec_from_long(type, v, &maxctx, &status);
     if (dec == NULL) {
         return NULL;
     }
index 3aadfb0..f45e558 100644 (file)
    PEP-3101 formatting for numeric types. */
 
 
+/* Disable warning that is part of -Wextra since gcc 7.0. */
+#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && __GNUC__ >= 7
+  #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
+#endif
+
+
 /*
  * Work around the behavior of tolower() and strcasecmp() in certain
  * locales. For example, in tr_TR.utf8:
index bef702e..a6d6c61 100644 (file)
@@ -627,6 +627,7 @@ element_gc_clear(ElementObject *self)
 static void
 element_dealloc(ElementObject* self)
 {
+    /* bpo-31095: UnTrack is needed before calling any callbacks */
     PyObject_GC_UnTrack(self);
     Py_TRASHCAN_SAFE_BEGIN(self)
 
@@ -2048,6 +2049,8 @@ elementiter_dealloc(ElementIterObject *it)
 {
     Py_ssize_t i = it->parent_stack_used;
     it->parent_stack_used = 0;
+    /* bpo-31095: UnTrack is needed before calling any callbacks */
+    PyObject_GC_UnTrack(it);
     while (i--)
         Py_XDECREF(it->parent_stack[i].parent);
     PyMem_Free(it->parent_stack);
@@ -2055,7 +2058,6 @@ elementiter_dealloc(ElementIterObject *it)
     Py_XDECREF(it->sought_tag);
     Py_XDECREF(it->root_element);
 
-    PyObject_GC_UnTrack(it);
     PyObject_GC_Del(it);
 }
 
@@ -3222,6 +3224,18 @@ xmlparser_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
     return (PyObject *)self;
 }
 
+static int
+ignore_attribute_error(PyObject *value)
+{
+    if (value == NULL) {
+        if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
+            return -1;
+        }
+        PyErr_Clear();
+    }
+    return 0;
+}
+
 /*[clinic input]
 _elementtree.XMLParser.__init__
 
@@ -3268,14 +3282,33 @@ _elementtree_XMLParser___init___impl(XMLParserObject *self, PyObject *html,
     self->target = target;
 
     self->handle_start = PyObject_GetAttrString(target, "start");
+    if (ignore_attribute_error(self->handle_start)) {
+        return -1;
+    }
     self->handle_data = PyObject_GetAttrString(target, "data");
+    if (ignore_attribute_error(self->handle_data)) {
+        return -1;
+    }
     self->handle_end = PyObject_GetAttrString(target, "end");
+    if (ignore_attribute_error(self->handle_end)) {
+        return -1;
+    }
     self->handle_comment = PyObject_GetAttrString(target, "comment");
+    if (ignore_attribute_error(self->handle_comment)) {
+        return -1;
+    }
     self->handle_pi = PyObject_GetAttrString(target, "pi");
+    if (ignore_attribute_error(self->handle_pi)) {
+        return -1;
+    }
     self->handle_close = PyObject_GetAttrString(target, "close");
+    if (ignore_attribute_error(self->handle_close)) {
+        return -1;
+    }
     self->handle_doctype = PyObject_GetAttrString(target, "doctype");
-
-    PyErr_Clear();
+    if (ignore_attribute_error(self->handle_doctype)) {
+        return -1;
+    }
 
     /* configure parser */
     EXPAT(SetUserData)(self->parser, self);
@@ -3334,7 +3367,11 @@ xmlparser_gc_traverse(XMLParserObject *self, visitproc visit, void *arg)
 static int
 xmlparser_gc_clear(XMLParserObject *self)
 {
-    EXPAT(ParserFree)(self->parser);
+    if (self->parser != NULL) {
+        XML_Parser parser = self->parser;
+        self->parser = NULL;
+        EXPAT(ParserFree)(parser);
+    }
 
     Py_CLEAR(self->handle_close);
     Py_CLEAR(self->handle_pi);
@@ -3947,6 +3984,11 @@ PyInit__elementtree(void)
     st->deepcopy_obj = PyObject_GetAttrString(temp, "deepcopy");
     Py_XDECREF(temp);
 
+    if (st->deepcopy_obj == NULL) {
+        return NULL;
+    }
+
+    assert(!PyErr_Occurred());
     if (!(st->elementpath_obj = PyImport_ImportModule("xml.etree.ElementPath")))
         return NULL;
 
index 1bcf16a..5622e2a 100644 (file)
@@ -116,6 +116,7 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw)
 static void
 partial_dealloc(partialobject *pto)
 {
+    /* bpo-31095: UnTrack is needed before calling any callbacks */
     PyObject_GC_UnTrack(pto);
     if (pto->weakreflist != NULL)
         PyObject_ClearWeakRefs((PyObject *) pto);
@@ -1038,7 +1039,11 @@ lru_cache_clear_list(lru_list_elem *link)
 static void
 lru_cache_dealloc(lru_cache_object *obj)
 {
-    lru_list_elem *list = lru_cache_unlink_list(obj);
+    lru_list_elem *list;
+    /* bpo-31095: UnTrack is needed before calling any callbacks */
+    PyObject_GC_UnTrack(obj);
+
+    list = lru_cache_unlink_list(obj);
     Py_XDECREF(obj->maxsize_O);
     Py_XDECREF(obj->func);
     Py_XDECREF(obj->cache);
index daa4f3d..5a86376 100644 (file)
@@ -139,7 +139,10 @@ EVP_hash(EVPobject *self, const void *vp, Py_ssize_t len)
             process = MUNCH_SIZE;
         else
             process = Py_SAFE_DOWNCAST(len, Py_ssize_t, unsigned int);
-        EVP_DigestUpdate(self->ctx, (const void*)cp, process);
+        if (!EVP_DigestUpdate(self->ctx, (const void*)cp, process)) {
+            _setException(PyExc_ValueError);
+            break;
+        }
         len -= process;
         cp += process;
     }
@@ -209,7 +212,10 @@ EVP_digest(EVPobject *self, PyObject *unused)
         return _setException(PyExc_ValueError);
     }
     digest_size = EVP_MD_CTX_size(temp_ctx);
-    EVP_DigestFinal(temp_ctx, digest, NULL);
+    if (!EVP_DigestFinal(temp_ctx, digest, NULL)) {
+        _setException(PyExc_ValueError);
+        return NULL;
+    }
 
     retval = PyBytes_FromStringAndSize((const char *)digest, digest_size);
     EVP_MD_CTX_free(temp_ctx);
@@ -237,7 +243,10 @@ EVP_hexdigest(EVPobject *self, PyObject *unused)
         return _setException(PyExc_ValueError);
     }
     digest_size = EVP_MD_CTX_size(temp_ctx);
-    EVP_DigestFinal(temp_ctx, digest, NULL);
+    if (!EVP_DigestFinal(temp_ctx, digest, NULL)) {
+        _setException(PyExc_ValueError);
+        return NULL;
+    }
 
     EVP_MD_CTX_free(temp_ctx);
 
@@ -362,7 +371,12 @@ EVP_tp_init(EVPobject *self, PyObject *args, PyObject *kwds)
             PyBuffer_Release(&view);
         return -1;
     }
-    EVP_DigestInit(self->ctx, digest);
+    if (!EVP_DigestInit(self->ctx, digest)) {
+        _setException(PyExc_ValueError);
+        if (data_obj)
+            PyBuffer_Release(&view);
+        return -1;
+    }
 
     self->name = name_obj;
     Py_INCREF(self->name);
@@ -461,7 +475,11 @@ EVPnew(PyObject *name_obj,
     if (initial_ctx) {
         EVP_MD_CTX_copy(self->ctx, initial_ctx);
     } else {
-        EVP_DigestInit(self->ctx, digest);
+        if (!EVP_DigestInit(self->ctx, digest)) {
+            _setException(PyExc_ValueError);
+            Py_DECREF(self);
+            return NULL;
+        }
     }
 
     if (cp && len) {
@@ -902,6 +920,8 @@ generate_hash_name_list(void)
  *  the generic one passing it a python string and are noticeably
  *  faster than calling a python new() wrapper.  Thats important for
  *  code that wants to make hashes of a bunch of small strings.
+ *  The first call will lazy-initialize, which reports an exception
+ *  if initialization fails.
  */
 #define GEN_CONSTRUCTOR(NAME)  \
     static PyObject * \
@@ -915,6 +935,17 @@ generate_hash_name_list(void)
             return NULL; \
         } \
      \
+        if (CONST_new_ ## NAME ## _ctx_p == NULL) { \
+            EVP_MD_CTX *ctx_p = EVP_MD_CTX_new(); \
+            if (!EVP_get_digestbyname(#NAME) || \
+                !EVP_DigestInit(ctx_p, EVP_get_digestbyname(#NAME))) { \
+                _setException(PyExc_ValueError); \
+                EVP_MD_CTX_free(ctx_p); \
+                return NULL; \
+            } \
+            CONST_new_ ## NAME ## _ctx_p = ctx_p; \
+        } \
+     \
         if (data_obj) \
             GET_BUFFER_VIEW_OR_ERROUT(data_obj, &view); \
      \
@@ -942,10 +973,6 @@ generate_hash_name_list(void)
 #define INIT_CONSTRUCTOR_CONSTANTS(NAME)  do { \
     if (CONST_ ## NAME ## _name_obj == NULL) { \
         CONST_ ## NAME ## _name_obj = PyUnicode_FromString(#NAME); \
-        if (EVP_get_digestbyname(#NAME)) { \
-            CONST_new_ ## NAME ## _ctx_p = EVP_MD_CTX_new(); \
-            EVP_DigestInit(CONST_new_ ## NAME ## _ctx_p, EVP_get_digestbyname(#NAME)); \
-        } \
     } \
 } while (0);
 
@@ -995,8 +1022,11 @@ PyInit__hashlib(void)
 {
     PyObject *m, *openssl_md_meth_names;
 
-    OpenSSL_add_all_digests();
+#ifndef OPENSSL_VERSION_1_1
+    /* Load all digest algorithms and initialize cpuid */
+    OPENSSL_add_all_algorithms_noconf();
     ERR_load_crypto_strings();
+#endif
 
     /* TODO build EVP_functions openssl_* entries dynamically based
      * on what hashes are supported rather than listing many
index a1ba121..6c54de7 100644 (file)
@@ -1131,6 +1131,8 @@ bytesiobuf_traverse(bytesiobuf *self, visitproc visit, void *arg)
 static void
 bytesiobuf_dealloc(bytesiobuf *self)
 {
+    /* bpo-31095: UnTrack is needed before calling any callbacks */
+    PyObject_GC_UnTrack(self);
     Py_CLEAR(self->source);
     Py_TYPE(self)->tp_free(self);
 }
index 833ea8e..918fa57 100644 (file)
@@ -280,11 +280,10 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode,
 
     if (fd < 0) {
 #ifdef MS_WINDOWS
-        Py_ssize_t length;
         if (!PyUnicode_FSDecoder(nameobj, &stringobj)) {
             return -1;
         }
-        widename = PyUnicode_AsUnicodeAndSize(stringobj, &length);
+        widename = PyUnicode_AsUnicode(stringobj);
         if (widename == NULL)
             return -1;
 #else
index bc8d11e..588c697 100644 (file)
@@ -538,6 +538,12 @@ _io_IncrementalNewlineDecoder_getstate_impl(nldecoder_object *self)
            _PyIO_str_getstate, NULL);
         if (state == NULL)
             return NULL;
+        if (!PyTuple_Check(state)) {
+            PyErr_SetString(PyExc_TypeError,
+                            "illegal decoder state");
+            Py_DECREF(state);
+            return NULL;
+        }
         if (!PyArg_ParseTuple(state, "OK", &buffer, &flag)) {
             Py_DECREF(state);
             return NULL;
@@ -569,6 +575,10 @@ _io_IncrementalNewlineDecoder_setstate(nldecoder_object *self,
     PyObject *buffer;
     unsigned long long flag;
 
+    if (!PyTuple_Check(state)) {
+        PyErr_SetString(PyExc_TypeError, "state argument must be a tuple");
+        return NULL;
+    }
     if (!PyArg_ParseTuple(state, "OK", &buffer, &flag))
         return NULL;
 
@@ -1321,6 +1331,13 @@ _io_TextIOWrapper_write_impl(textio *self, PyObject *text)
     Py_DECREF(text);
     if (b == NULL)
         return NULL;
+    if (!PyBytes_Check(b)) {
+        PyErr_Format(PyExc_TypeError,
+                     "encoder should return a bytes object, not '%.200s'",
+                     Py_TYPE(b)->tp_name);
+        Py_DECREF(b);
+        return NULL;
+    }
 
     if (self->pending_bytes == NULL) {
         self->pending_bytes = PyList_New(0);
@@ -1440,15 +1457,23 @@ textiowrapper_read_chunk(textio *self, Py_ssize_t size_hint)
         /* Given this, we know there was a valid snapshot point
          * len(dec_buffer) bytes ago with decoder state (b'', dec_flags).
          */
-        if (PyArg_ParseTuple(state, "OO", &dec_buffer, &dec_flags) < 0) {
+        if (!PyTuple_Check(state)) {
+            PyErr_SetString(PyExc_TypeError,
+                            "illegal decoder state");
+            Py_DECREF(state);
+            return -1;
+        }
+        if (!PyArg_ParseTuple(state,
+                              "OO;illegal decoder state", &dec_buffer, &dec_flags))
+        {
             Py_DECREF(state);
             return -1;
         }
 
         if (!PyBytes_Check(dec_buffer)) {
             PyErr_Format(PyExc_TypeError,
-                         "decoder getstate() should have returned a bytes "
-                         "object, not '%.200s'",
+                         "illegal decoder state: the first item should be a "
+                         "bytes object, not '%.200s'",
                          Py_TYPE(dec_buffer)->tp_name);
             Py_DECREF(state);
             return -1;
@@ -2320,14 +2345,20 @@ _io_TextIOWrapper_tell_impl(textio *self)
             _PyIO_str_getstate, NULL); \
         if (_state == NULL) \
             goto fail; \
+        if (!PyTuple_Check(_state)) { \
+            PyErr_SetString(PyExc_TypeError, \
+                            "illegal decoder state"); \
+            Py_DECREF(_state); \
+            goto fail; \
+        } \
         if (!PyArg_ParseTuple(_state, "Oi", &dec_buffer, &dec_flags)) { \
             Py_DECREF(_state); \
             goto fail; \
         } \
         if (!PyBytes_Check(dec_buffer)) { \
             PyErr_Format(PyExc_TypeError, \
-                         "decoder getstate() should have returned a bytes " \
-                         "object, not '%.200s'", \
+                         "illegal decoder state: the first item should be a " \
+                         "bytes object, not '%.200s'", \
                          Py_TYPE(dec_buffer)->tp_name); \
             Py_DECREF(_state); \
             goto fail; \
index 94dfe3e..5cf3f07 100644 (file)
@@ -79,7 +79,7 @@ char _PyIO_get_console_type(PyObject *path_or_fd) {
         PyErr_Clear();
         return '\0';
     }
-    decoded_wstr = PyUnicode_AsWideCharString(decoded, NULL);
+    decoded_wstr = _PyUnicode_AsWideCharString(decoded);
     Py_CLEAR(decoded);
     if (!decoded_wstr) {
         PyErr_Clear();
@@ -311,23 +311,17 @@ _io__WindowsConsoleIO___init___impl(winconsoleio *self, PyObject *nameobj,
         if (!d)
             return -1;
 
-        Py_ssize_t length;
-        name = PyUnicode_AsWideCharString(decodedname, &length);
+        name = _PyUnicode_AsWideCharString(decodedname);
         console_type = _PyIO_get_console_type(decodedname);
         Py_CLEAR(decodedname);
         if (name == NULL)
             return -1;
         if (console_type == '\0') {
+            PyMem_Free(name);
             PyErr_SetString(PyExc_ValueError,
                 "Cannot open non-console file");
             return -1;
         }
-
-        if (wcslen(name) != length) {
-            PyMem_Free(name);
-            PyErr_SetString(PyExc_ValueError, "embedded null character");
-            return -1;
-        }
     }
 
     s = mode;
@@ -407,7 +401,7 @@ _io__WindowsConsoleIO___init___impl(winconsoleio *self, PyObject *nameobj,
         PyErr_SetString(PyExc_ValueError,
             "Cannot open non-console file");
         goto error;
-    }    
+    }
     if (self->writable && console_type != 'w') {
         PyErr_SetString(PyExc_ValueError,
             "Cannot open console input buffer for writing");
@@ -435,8 +429,7 @@ error:
     internal_close(self);
 
 done:
-    if (name)
-        PyMem_Free(name);
+    PyMem_Free(name);
     return ret;
 }
 
index a84b085..59376a7 100644 (file)
@@ -655,7 +655,8 @@ py_encode_basestring(PyObject* self UNUSED, PyObject *pystr)
 static void
 scanner_dealloc(PyObject *self)
 {
-    /* Deallocate scanner object */
+    /* bpo-31095: UnTrack is needed before calling any callbacks */
+    PyObject_GC_UnTrack(self);
     scanner_clear(self);
     Py_TYPE(self)->tp_free(self);
 }
@@ -1601,8 +1602,10 @@ encoder_listencode_dict(PyEncoderObject *s, _PyAccu *acc,
     if (items == NULL)
         goto bail;
     sortkeys = PyObject_IsTrue(s->sort_keys);
-    if (sortkeys < 0 || (sortkeys && PyList_Sort(items) < 0))
+    if (sortkeys < 0 || (sortkeys && PyList_Sort(items) < 0)) {
+        Py_DECREF(items);
         goto bail;
+    }
     it = PyObject_GetIter(items);
     Py_DECREF(items);
     if (it == NULL)
@@ -1791,7 +1794,8 @@ bail:
 static void
 encoder_dealloc(PyObject *self)
 {
-    /* Deallocate Encoder */
+    /* bpo-31095: UnTrack is needed before calling any callbacks */
+    PyObject_GC_UnTrack(self);
     encoder_clear(self);
     Py_TYPE(self)->tp_free(self);
 }
index feb3802..71c9146 100644 (file)
@@ -215,10 +215,10 @@ PyLocale_strcoll(PyObject* self, PyObject* args)
     if (!PyArg_ParseTuple(args, "UU:strcoll", &os1, &os2))
         return NULL;
     /* Convert the unicode strings to wchar[]. */
-    ws1 = PyUnicode_AsWideCharString(os1, NULL);
+    ws1 = _PyUnicode_AsWideCharString(os1);
     if (ws1 == NULL)
         goto done;
-    ws2 = PyUnicode_AsWideCharString(os2, NULL);
+    ws2 = _PyUnicode_AsWideCharString(os2);
     if (ws2 == NULL)
         goto done;
     /* Collate the strings. */
@@ -252,6 +252,11 @@ PyLocale_strxfrm(PyObject* self, PyObject* args)
     s = PyUnicode_AsWideCharString(str, &n1);
     if (s == NULL)
         goto exit;
+    if (wcslen(s) != (size_t)n1) {
+        PyErr_SetString(PyExc_ValueError,
+                        "embedded null character");
+        goto exit;
+    }
 
     /* assume no change in size, first */
     n1 = n1 + 1;
index 920b46f..ef0a03b 100644 (file)
@@ -1705,7 +1705,7 @@ whichmodule(PyObject *global, PyObject *dotted_path)
 
     /* If no module is found, use __main__. */
     module_name = _PyUnicode_FromId(&PyId___main__);
-    Py_INCREF(module_name);
+    Py_XINCREF(module_name);
     return module_name;
 }
 
index 0fffaac..a32fce4 100644 (file)
@@ -279,7 +279,7 @@ static unsigned int _ssl_locks_count = 0;
 typedef struct {
     PyObject_HEAD
     SSL_CTX *ctx;
-#ifdef OPENSSL_NPN_NEGOTIATED
+#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
     unsigned char *npn_protocols;
     int npn_protocols_len;
 #endif
@@ -298,12 +298,15 @@ typedef struct {
     PyObject *Socket; /* weakref to socket on which we're layered */
     SSL *ssl;
     PySSLContext *ctx; /* weakref to SSL context */
-    X509 *peer_cert;
     char shutdown_seen_zero;
-    char handshake_done;
     enum py_ssl_server_or_client socket_type;
     PyObject *owner; /* Python level "owner" passed to servername callback */
     PyObject *server_hostname;
+    int ssl_errno; /* last seen error from SSL */
+    int c_errno; /* last seen error from libc */
+#ifdef MS_WINDOWS
+    int ws_errno; /* last seen error from winsock */
+#endif
 } PySSLSocket;
 
 typedef struct {
@@ -323,6 +326,20 @@ static PyTypeObject PySSLSocket_Type;
 static PyTypeObject PySSLMemoryBIO_Type;
 static PyTypeObject PySSLSession_Type;
 
+#ifdef MS_WINDOWS
+#define _PySSL_UPDATE_ERRNO_IF(cond, sock, retcode) if (cond) { \
+        (sock)->ws_errno = WSAGetLastError(); \
+        (sock)->c_errno = errno; \
+        (sock)->ssl_errno = SSL_get_error((sock->ssl), (retcode)); \
+    } else { sock->ws_errno = 0; sock->c_errno = 0; sock->ssl_errno = 0; }
+#else
+#define _PySSL_UPDATE_ERRNO_IF(cond, sock, retcode) if (cond) { \
+        (sock)->c_errno = errno; \
+        (sock)->ssl_errno = SSL_get_error((sock->ssl), (retcode)); \
+    } else { (sock)->c_errno = 0; (sock)->ssl_errno = 0; }
+#endif
+#define _PySSL_UPDATE_ERRNO(sock, retcode) _PySSL_UPDATE_ERRNO_IF(1, (sock), (retcode))
+
 /*[clinic input]
 module _ssl
 class _ssl._SSLContext "PySSLContext *" "&PySSLContext_Type"
@@ -496,7 +513,7 @@ PySSL_SetError(PySSLSocket *obj, int ret, const char *filename, int lineno)
     e = ERR_peek_last_error();
 
     if (obj->ssl != NULL) {
-        err = SSL_get_error(obj->ssl, ret);
+        err = obj->ssl_errno;
 
         switch (err) {
         case SSL_ERROR_ZERO_RETURN:
@@ -532,8 +549,16 @@ PySSL_SetError(PySSLSocket *obj, int ret, const char *filename, int lineno)
                     errstr = "EOF occurred in violation of protocol";
                 } else if (s && ret == -1) {
                     /* underlying BIO reported an I/O error */
-                    Py_INCREF(s);
                     ERR_clear_error();
+#ifdef MS_WINDOWS
+                    if (obj->ws_errno)
+                        return PyErr_SetFromWindowsErr(obj->ws_errno);
+#endif
+                    if (obj->c_errno) {
+                        errno = obj->c_errno;
+                        return PyErr_SetFromErrno(PyExc_OSError);
+                    }
+                    Py_INCREF(s);
                     s->errorhandler();
                     Py_DECREF(s);
                     return NULL;
@@ -595,13 +620,11 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
     if (self == NULL)
         return NULL;
 
-    self->peer_cert = NULL;
     self->ssl = NULL;
     self->Socket = NULL;
     self->ctx = sslctx;
     Py_INCREF(sslctx);
     self->shutdown_seen_zero = 0;
-    self->handshake_done = 0;
     self->owner = NULL;
     self->server_hostname = NULL;
     if (server_hostname != NULL) {
@@ -613,6 +636,11 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
         }
         self->server_hostname = hostname;
     }
+    self->ssl_errno = 0;
+    self->c_errno = 0;
+#ifdef MS_WINDOWS
+    self->ws_errno = 0;
+#endif
 
     /* Make sure the SSL error state is initialized */
     (void) ERR_get_state();
@@ -710,8 +738,9 @@ _ssl__SSLSocket_do_handshake_impl(PySSLSocket *self)
     do {
         PySSL_BEGIN_ALLOW_THREADS
         ret = SSL_do_handshake(self->ssl);
-        err = SSL_get_error(self->ssl, ret);
+        _PySSL_UPDATE_ERRNO_IF(ret < 1, self, ret);
         PySSL_END_ALLOW_THREADS
+        err = self->ssl_errno;
 
         if (PyErr_CheckSignals())
             goto error;
@@ -747,15 +776,7 @@ _ssl__SSLSocket_do_handshake_impl(PySSLSocket *self)
     if (ret < 1)
         return PySSL_SetError(self, ret, __FILE__, __LINE__);
 
-    if (self->peer_cert)
-        X509_free (self->peer_cert);
-    PySSL_BEGIN_ALLOW_THREADS
-    self->peer_cert = SSL_get_peer_certificate(self->ssl);
-    PySSL_END_ALLOW_THREADS
-    self->handshake_done = 1;
-
-    Py_INCREF(Py_None);
-    return Py_None;
+    Py_RETURN_NONE;
 
 error:
     Py_XDECREF(sock);
@@ -763,49 +784,64 @@ error:
 }
 
 static PyObject *
-_create_tuple_for_attribute (ASN1_OBJECT *name, ASN1_STRING *value) {
-
-    char namebuf[X509_NAME_MAXLEN];
+_asn1obj2py(const ASN1_OBJECT *name, int no_name)
+{
+    char buf[X509_NAME_MAXLEN];
+    char *namebuf = buf;
     int buflen;
-    PyObject *name_obj;
-    PyObject *value_obj;
-    PyObject *attr;
-    unsigned char *valuebuf = NULL;
+    PyObject *name_obj = NULL;
 
-    buflen = OBJ_obj2txt(namebuf, sizeof(namebuf), name, 0);
+    buflen = OBJ_obj2txt(namebuf, X509_NAME_MAXLEN, name, no_name);
     if (buflen < 0) {
         _setSSLError(NULL, 0, __FILE__, __LINE__);
-        goto fail;
+        return NULL;
     }
-    name_obj = PyUnicode_FromStringAndSize(namebuf, buflen);
-    if (name_obj == NULL)
-        goto fail;
+    /* initial buffer is too small for oid + terminating null byte */
+    if (buflen > X509_NAME_MAXLEN - 1) {
+        /* make OBJ_obj2txt() calculate the required buflen */
+        buflen = OBJ_obj2txt(NULL, 0, name, no_name);
+        /* allocate len + 1 for terminating NULL byte */
+        namebuf = PyMem_Malloc(buflen + 1);
+        if (namebuf == NULL) {
+            PyErr_NoMemory();
+            return NULL;
+        }
+        buflen = OBJ_obj2txt(namebuf, buflen + 1, name, no_name);
+        if (buflen < 0) {
+            _setSSLError(NULL, 0, __FILE__, __LINE__);
+            goto done;
+        }
+    }
+    if (!buflen && no_name) {
+        Py_INCREF(Py_None);
+        name_obj = Py_None;
+    }
+    else {
+        name_obj = PyUnicode_FromStringAndSize(namebuf, buflen);
+    }
+
+  done:
+    if (buf != namebuf) {
+        PyMem_Free(namebuf);
+    }
+    return name_obj;
+}
+
+static PyObject *
+_create_tuple_for_attribute(ASN1_OBJECT *name, ASN1_STRING *value)
+{
+    Py_ssize_t buflen;
+    unsigned char *valuebuf = NULL;
+    PyObject *attr;
 
     buflen = ASN1_STRING_to_UTF8(&valuebuf, value);
     if (buflen < 0) {
         _setSSLError(NULL, 0, __FILE__, __LINE__);
-        Py_DECREF(name_obj);
-        goto fail;
+        return NULL;
     }
-    value_obj = PyUnicode_DecodeUTF8((char *) valuebuf,
-                                     buflen, "strict");
+    attr = Py_BuildValue("Ns#", _asn1obj2py(name, 0), valuebuf, buflen);
     OPENSSL_free(valuebuf);
-    if (value_obj == NULL) {
-        Py_DECREF(name_obj);
-        goto fail;
-    }
-    attr = PyTuple_New(2);
-    if (attr == NULL) {
-        Py_DECREF(name_obj);
-        Py_DECREF(value_obj);
-        goto fail;
-    }
-    PyTuple_SET_ITEM(attr, 0, name_obj);
-    PyTuple_SET_ITEM(attr, 1, value_obj);
     return attr;
-
-  fail:
-    return NULL;
 }
 
 static PyObject *
@@ -1506,25 +1542,30 @@ _ssl__SSLSocket_peer_certificate_impl(PySSLSocket *self, int binary_mode)
 /*[clinic end generated code: output=f0dc3e4d1d818a1d input=8281bd1d193db843]*/
 {
     int verification;
+    X509 *peer_cert;
+    PyObject *result;
 
-    if (!self->handshake_done) {
+    if (!SSL_is_init_finished(self->ssl)) {
         PyErr_SetString(PyExc_ValueError,
                         "handshake not done yet");
         return NULL;
     }
-    if (!self->peer_cert)
+    peer_cert = SSL_get_peer_certificate(self->ssl);
+    if (peer_cert == NULL)
         Py_RETURN_NONE;
 
     if (binary_mode) {
         /* return cert in DER-encoded format */
-        return _certificate_to_der(self->peer_cert);
+        result = _certificate_to_der(peer_cert);
     } else {
         verification = SSL_CTX_get_verify_mode(SSL_get_SSL_CTX(self->ssl));
         if ((verification & SSL_VERIFY_PEER) == 0)
-            return PyDict_New();
+            result = PyDict_New();
         else
-            return _decode_certificate(self->peer_cert);
+            result = _decode_certificate(peer_cert);
     }
+    X509_free(peer_cert);
+    return result;
 }
 
 static PyObject *
@@ -1687,13 +1728,17 @@ _ssl__SSLSocket_version_impl(PySSLSocket *self)
 
     if (self->ssl == NULL)
         Py_RETURN_NONE;
+    if (!SSL_is_init_finished(self->ssl)) {
+        /* handshake not finished */
+        Py_RETURN_NONE;
+    }
     version = SSL_get_version(self->ssl);
     if (!strcmp(version, "unknown"))
         Py_RETURN_NONE;
     return PyUnicode_FromString(version);
 }
 
-#ifdef OPENSSL_NPN_NEGOTIATED
+#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
 /*[clinic input]
 _ssl._SSLSocket.selected_npn_protocol
 [clinic start generated code]*/
@@ -1845,8 +1890,6 @@ Passed as \"self\" in servername callback.");
 
 static void PySSL_dealloc(PySSLSocket *self)
 {
-    if (self->peer_cert)        /* Possible not to have one? */
-        X509_free (self->peer_cert);
     if (self->ssl)
         SSL_free(self->ssl);
     Py_XDECREF(self->Socket);
@@ -1993,8 +2036,9 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b)
     do {
         PySSL_BEGIN_ALLOW_THREADS
         len = SSL_write(self->ssl, b->buf, (int)b->len);
-        err = SSL_get_error(self->ssl, len);
+        _PySSL_UPDATE_ERRNO_IF(len <= 0, self, len);
         PySSL_END_ALLOW_THREADS
+        err = self->ssl_errno;
 
         if (PyErr_CheckSignals())
             goto error;
@@ -2048,6 +2092,7 @@ _ssl__SSLSocket_pending_impl(PySSLSocket *self)
 
     PySSL_BEGIN_ALLOW_THREADS
     count = SSL_pending(self->ssl);
+    _PySSL_UPDATE_ERRNO_IF(count < 0, self, count);
     PySSL_END_ALLOW_THREADS
     if (count < 0)
         return PySSL_SetError(self, count, __FILE__, __LINE__);
@@ -2136,7 +2181,7 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, int len, int group_right_1,
     do {
         PySSL_BEGIN_ALLOW_THREADS
         count = SSL_read(self->ssl, mem, len);
-        err = SSL_get_error(self->ssl, count);
+        _PySSL_UPDATE_ERRNO_IF(count <= 0, self, count);
         PySSL_END_ALLOW_THREADS
 
         if (PyErr_CheckSignals())
@@ -2145,6 +2190,7 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, int len, int group_right_1,
         if (has_timeout)
             timeout = deadline - _PyTime_GetMonotonicClock();
 
+        err = self->ssl_errno;
         if (err == SSL_ERROR_WANT_READ) {
             sockstate = PySSL_select(sock, 0, timeout);
         } else if (err == SSL_ERROR_WANT_WRITE) {
@@ -2201,7 +2247,7 @@ static PyObject *
 _ssl__SSLSocket_shutdown_impl(PySSLSocket *self)
 /*[clinic end generated code: output=ca1aa7ed9d25ca42 input=ede2cc1a2ddf0ee4]*/
 {
-    int err, ssl_err, sockstate, nonblocking;
+    int err, sockstate, nonblocking;
     int zeros = 0;
     PySocketSockObject *sock = GET_SOCKET(self);
     _PyTime_t timeout, deadline = 0;
@@ -2240,6 +2286,7 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self)
         if (self->shutdown_seen_zero)
             SSL_set_read_ahead(self->ssl, 0);
         err = SSL_shutdown(self->ssl);
+        _PySSL_UPDATE_ERRNO_IF(err < 0, self, err);
         PySSL_END_ALLOW_THREADS
 
         /* If err == 1, a secure shutdown with SSL_shutdown() is complete */
@@ -2260,16 +2307,16 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self)
             timeout = deadline - _PyTime_GetMonotonicClock();
 
         /* Possibly retry shutdown until timeout or failure */
-        ssl_err = SSL_get_error(self->ssl, err);
-        if (ssl_err == SSL_ERROR_WANT_READ)
+        _PySSL_UPDATE_ERRNO(self, err);
+        if (self->ssl_errno == SSL_ERROR_WANT_READ)
             sockstate = PySSL_select(sock, 0, timeout);
-        else if (ssl_err == SSL_ERROR_WANT_WRITE)
+        else if (self->ssl_errno == SSL_ERROR_WANT_WRITE)
             sockstate = PySSL_select(sock, 1, timeout);
         else
             break;
 
         if (sockstate == SOCKET_HAS_TIMED_OUT) {
-            if (ssl_err == SSL_ERROR_WANT_READ)
+            if (self->ssl_errno == SSL_ERROR_WANT_READ)
                 PyErr_SetString(PySocketModule.timeout_error,
                                 "The read operation timed out");
             else
@@ -2442,7 +2489,7 @@ static int PySSL_set_session(PySSLSocket *self, PyObject *value,
                         "Cannot set session for server-side SSLSocket.");
         return -1;
     }
-    if (self->handshake_done) {
+    if (SSL_is_init_finished(self->ssl)) {
         PyErr_SetString(PyExc_ValueError,
                         "Cannot set session after handshake.");
         return -1;
@@ -2633,8 +2680,7 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
         return NULL;
     }
     if (ctx == NULL) {
-        PyErr_SetString(PySSLErrorObject,
-                        "failed to allocate SSL context");
+        _setSSLError(NULL, 0, __FILE__, __LINE__);
         return NULL;
     }
 
@@ -2645,7 +2691,7 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
         return NULL;
     }
     self->ctx = ctx;
-#ifdef OPENSSL_NPN_NEGOTIATED
+#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
     self->npn_protocols = NULL;
 #endif
 #ifdef HAVE_ALPN
@@ -2776,9 +2822,11 @@ context_clear(PySSLContext *self)
 static void
 context_dealloc(PySSLContext *self)
 {
+    /* bpo-31095: UnTrack is needed before calling any callbacks */
+    PyObject_GC_UnTrack(self);
     context_clear(self);
     SSL_CTX_free(self->ctx);
-#ifdef OPENSSL_NPN_NEGOTIATED
+#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
     PyMem_FREE(self->npn_protocols);
 #endif
 #ifdef HAVE_ALPN
@@ -2856,7 +2904,7 @@ _ssl__SSLContext_get_ciphers_impl(PySSLContext *self)
 #endif
 
 
-#ifdef OPENSSL_NPN_NEGOTIATED
+#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG) || defined(HAVE_ALPN)
 static int
 do_protocol_selection(int alpn, unsigned char **out, unsigned char *outlen,
                       const unsigned char *server_protocols, unsigned int server_protocols_len,
@@ -2880,7 +2928,9 @@ do_protocol_selection(int alpn, unsigned char **out, unsigned char *outlen,
 
     return SSL_TLSEXT_ERR_OK;
 }
+#endif
 
+#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
 /* this callback gets passed to SSL_CTX_set_next_protos_advertise_cb */
 static int
 _advertiseNPN_cb(SSL *s,
@@ -2923,7 +2973,7 @@ _ssl__SSLContext__set_npn_protocols_impl(PySSLContext *self,
                                          Py_buffer *protos)
 /*[clinic end generated code: output=72b002c3324390c6 input=319fcb66abf95bd7]*/
 {
-#ifdef OPENSSL_NPN_NEGOTIATED
+#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
     PyMem_Free(self->npn_protocols);
     self->npn_protocols = PyMem_Malloc(protos->len);
     if (self->npn_protocols == NULL)
@@ -2974,6 +3024,12 @@ _ssl__SSLContext__set_alpn_protocols_impl(PySSLContext *self,
 /*[clinic end generated code: output=87599a7f76651a9b input=9bba964595d519be]*/
 {
 #ifdef HAVE_ALPN
+    if ((size_t)protos->len > UINT_MAX) {
+        PyErr_Format(PyExc_OverflowError,
+            "protocols longer than %d bytes", UINT_MAX);
+        return NULL;
+    }
+
     PyMem_FREE(self->alpn_protocols);
     self->alpn_protocols = PyMem_Malloc(protos->len);
     if (!self->alpn_protocols)
@@ -4284,6 +4340,7 @@ static PyTypeObject PySSLMemoryBIO_Type = {
 static void
 PySSLSession_dealloc(PySSLSession *self)
 {
+    /* bpo-31095: UnTrack is needed before calling any callbacks */
     PyObject_GC_UnTrack(self);
     Py_XDECREF(self->ctx);
     if (self->session != NULL) {
@@ -4664,8 +4721,6 @@ asn1obj2py(ASN1_OBJECT *obj)
 {
     int nid;
     const char *ln, *sn;
-    char buf[100];
-    Py_ssize_t buflen;
 
     nid = OBJ_obj2nid(obj);
     if (nid == NID_undef) {
@@ -4674,16 +4729,7 @@ asn1obj2py(ASN1_OBJECT *obj)
     }
     sn = OBJ_nid2sn(nid);
     ln = OBJ_nid2ln(nid);
-    buflen = OBJ_obj2txt(buf, sizeof(buf), obj, 1);
-    if (buflen < 0) {
-        _setSSLError(NULL, 0, __FILE__, __LINE__);
-        return NULL;
-    }
-    if (buflen) {
-        return Py_BuildValue("isss#", nid, sn, ln, buf, buflen);
-    } else {
-        return Py_BuildValue("issO", nid, sn, ln, Py_None);
-    }
+    return Py_BuildValue("issN", nid, sn, ln, _asn1obj2py(obj, 1));
 }
 
 /*[clinic input]
@@ -5166,9 +5212,14 @@ PyInit__ssl(void)
         return NULL;
     PySocketModule = *socket_api;
 
+#ifndef OPENSSL_VERSION_1_1
+    /* Load all algorithms and initialize cpuid */
+    OPENSSL_add_all_algorithms_noconf();
     /* Init OpenSSL */
     SSL_load_error_strings();
     SSL_library_init();
+#endif
+
 #ifdef WITH_THREAD
 #ifdef HAVE_OPENSSL_CRYPTO_LOCK
     /* note that this will start threading if not already started */
@@ -5180,7 +5231,6 @@ PyInit__ssl(void)
     _ssl_locks_count++;
 #endif
 #endif  /* WITH_THREAD */
-    OpenSSL_add_all_algorithms();
 
     /* Add symbols to module dict */
     sslerror_type_slots[0].pfunc = PyExc_OSError;
@@ -5356,6 +5406,11 @@ PyInit__ssl(void)
     PyModule_AddIntConstant(m, "OP_NO_TLSv1_1", SSL_OP_NO_TLSv1_1);
     PyModule_AddIntConstant(m, "OP_NO_TLSv1_2", SSL_OP_NO_TLSv1_2);
 #endif
+#ifdef SSL_OP_NO_TLSv1_3
+    PyModule_AddIntConstant(m, "OP_NO_TLSv1_3", SSL_OP_NO_TLSv1_3);
+#else
+    PyModule_AddIntConstant(m, "OP_NO_TLSv1_3", 0);
+#endif
     PyModule_AddIntConstant(m, "OP_CIPHER_SERVER_PREFERENCE",
                             SSL_OP_CIPHER_SERVER_PREFERENCE);
     PyModule_AddIntConstant(m, "OP_SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE);
@@ -5388,7 +5443,7 @@ PyInit__ssl(void)
     Py_INCREF(r);
     PyModule_AddObject(m, "HAS_ECDH", r);
 
-#ifdef OPENSSL_NPN_NEGOTIATED
+#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
     r = Py_True;
 #else
     r = Py_False;
@@ -5404,6 +5459,14 @@ PyInit__ssl(void)
     Py_INCREF(r);
     PyModule_AddObject(m, "HAS_ALPN", r);
 
+#if defined(TLS1_3_VERSION) && !defined(OPENSSL_NO_TLS1_3)
+    r = Py_True;
+#else
+    r = Py_False;
+#endif
+    Py_INCREF(r);
+    PyModule_AddObject(m, "HAS_TLSv1_3", r);
+
     /* Mappings for error codes */
     err_codes_to_names = PyDict_New();
     err_names_to_codes = PyDict_New();
index 2635af9..cd3fa2d 100644 (file)
@@ -1460,7 +1460,8 @@ s_init(PyObject *self, PyObject *args, PyObject *kwds)
     if (!PyBytes_Check(o_format)) {
         Py_DECREF(o_format);
         PyErr_Format(PyExc_TypeError,
-                     "Struct() argument 1 must be a bytes object, not %.200s",
+                     "Struct() argument 1 must be a str or bytes object, "
+                     "not %.200s",
                      Py_TYPE(o_format)->tp_name);
         return -1;
     }
@@ -1541,7 +1542,7 @@ s_unpack(PyObject *self, PyObject *input)
         return NULL;
     if (vbuf.len != soself->s_size) {
         PyErr_Format(StructError,
-                     "unpack requires a bytes object of length %zd",
+                     "unpack requires a buffer of %zd bytes",
                      soself->s_size);
         PyBuffer_Release(&vbuf);
         return NULL;
@@ -1605,6 +1606,8 @@ typedef struct {
 static void
 unpackiter_dealloc(unpackiterobject *self)
 {
+    /* bpo-31095: UnTrack is needed before calling any callbacks */
+    PyObject_GC_UnTrack(self);
     Py_XDECREF(self->so);
     PyBuffer_Release(&self->buf);
     PyObject_GC_Del(self);
@@ -1716,8 +1719,8 @@ s_iter_unpack(PyObject *_so, PyObject *input)
     }
     if (self->buf.len % so->s_size != 0) {
         PyErr_Format(StructError,
-                     "iterative unpacking requires a bytes length "
-                     "multiple of %zd",
+                     "iterative unpacking requires a buffer of "
+                     "a multiple of %zd bytes",
                      so->s_size);
         Py_DECREF(self);
         return NULL;
index c3d829c..ede675d 100644 (file)
 #  include <winsock2.h>         /* struct timeval */
 #endif
 
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>           /* For W_STOPCODE */
+#endif
+
 #ifdef WITH_THREAD
 #include "pythread.h"
 #endif /* WITH_THREAD */
@@ -866,8 +870,9 @@ test_L_code(PyObject *self)
     PyTuple_SET_ITEM(tuple, 0, num);
 
     value = -1;
-    if (PyArg_ParseTuple(tuple, "L:test_L_code", &value) < 0)
+    if (!PyArg_ParseTuple(tuple, "L:test_L_code", &value)) {
         return NULL;
+    }
     if (value != 42)
         return raiseTestError("test_L_code",
             "L code returned wrong value for long 42");
@@ -880,8 +885,9 @@ test_L_code(PyObject *self)
     PyTuple_SET_ITEM(tuple, 0, num);
 
     value = -1;
-    if (PyArg_ParseTuple(tuple, "L:test_L_code", &value) < 0)
+    if (!PyArg_ParseTuple(tuple, "L:test_L_code", &value)) {
         return NULL;
+    }
     if (value != 42)
         return raiseTestError("test_L_code",
             "L code returned wrong value for int 42");
@@ -1198,8 +1204,9 @@ test_k_code(PyObject *self)
     PyTuple_SET_ITEM(tuple, 0, num);
 
     value = 0;
-    if (PyArg_ParseTuple(tuple, "k:test_k_code", &value) < 0)
+    if (!PyArg_ParseTuple(tuple, "k:test_k_code", &value)) {
         return NULL;
+    }
     if (value != ULONG_MAX)
         return raiseTestError("test_k_code",
             "k code returned wrong value for long 0xFFF...FFF");
@@ -1217,8 +1224,9 @@ test_k_code(PyObject *self)
     PyTuple_SET_ITEM(tuple, 0, num);
 
     value = 0;
-    if (PyArg_ParseTuple(tuple, "k:test_k_code", &value) < 0)
+    if (!PyArg_ParseTuple(tuple, "k:test_k_code", &value)) {
         return NULL;
+    }
     if (value != (unsigned long)-0x42)
         return raiseTestError("test_k_code",
             "k code returned wrong value for long -0xFFF..000042");
@@ -1556,11 +1564,13 @@ test_s_code(PyObject *self)
     /* These two blocks used to raise a TypeError:
      * "argument must be string without null bytes, not str"
      */
-    if (PyArg_ParseTuple(tuple, "s:test_s_code1", &value) < 0)
-    return NULL;
+    if (!PyArg_ParseTuple(tuple, "s:test_s_code1", &value)) {
+        return NULL;
+    }
 
-    if (PyArg_ParseTuple(tuple, "z:test_s_code2", &value) < 0)
-    return NULL;
+    if (!PyArg_ParseTuple(tuple, "z:test_s_code2", &value)) {
+        return NULL;
+    }
 
     Py_DECREF(tuple);
     Py_RETURN_NONE;
@@ -1662,14 +1672,16 @@ test_u_code(PyObject *self)
     PyTuple_SET_ITEM(tuple, 0, obj);
 
     value = 0;
-    if (PyArg_ParseTuple(tuple, "u:test_u_code", &value) < 0)
+    if (!PyArg_ParseTuple(tuple, "u:test_u_code", &value)) {
         return NULL;
+    }
     if (value != PyUnicode_AS_UNICODE(obj))
         return raiseTestError("test_u_code",
             "u code returned wrong value for u'test'");
     value = 0;
-    if (PyArg_ParseTuple(tuple, "u#:test_u_code", &value, &len) < 0)
+    if (!PyArg_ParseTuple(tuple, "u#:test_u_code", &value, &len)) {
         return NULL;
+    }
     if (value != PyUnicode_AS_UNICODE(obj) ||
         len != PyUnicode_GET_SIZE(obj))
         return raiseTestError("test_u_code",
@@ -1702,8 +1714,9 @@ test_Z_code(PyObject *self)
     value2 = PyUnicode_AS_UNICODE(obj);
 
     /* Test Z for both values */
-    if (PyArg_ParseTuple(tuple, "ZZ:test_Z_code", &value1, &value2) < 0)
+    if (!PyArg_ParseTuple(tuple, "ZZ:test_Z_code", &value1, &value2)) {
         return NULL;
+    }
     if (value1 != PyUnicode_AS_UNICODE(obj))
         return raiseTestError("test_Z_code",
             "Z code returned wrong value for 'test'");
@@ -1717,9 +1730,11 @@ test_Z_code(PyObject *self)
     len2 = -1;
 
     /* Test Z# for both values */
-    if (PyArg_ParseTuple(tuple, "Z#Z#:test_Z_code", &value1, &len1,
-                         &value2, &len2) < 0)
+    if (!PyArg_ParseTuple(tuple, "Z#Z#:test_Z_code", &value1, &len1,
+                          &value2, &len2))
+    {
         return NULL;
+    }
     if (value1 != PyUnicode_AS_UNICODE(obj) ||
         len1 != PyUnicode_GET_SIZE(obj))
         return raiseTestError("test_Z_code",
@@ -2024,8 +2039,9 @@ test_empty_argparse(PyObject *self)
     tuple = PyTuple_New(0);
     if (!tuple)
         return NULL;
-    if ((result = PyArg_ParseTuple(tuple, "|:test_empty_argparse")) < 0)
+    if (!(result = PyArg_ParseTuple(tuple, "|:test_empty_argparse"))) {
         goto done;
+    }
     dict = PyDict_New();
     if (!dict)
         goto done;
@@ -2033,8 +2049,9 @@ test_empty_argparse(PyObject *self)
   done:
     Py_DECREF(tuple);
     Py_XDECREF(dict);
-    if (result < 0)
+    if (!result) {
         return NULL;
+    }
     else {
         Py_RETURN_NONE;
     }
@@ -3569,8 +3586,9 @@ test_raise_signal(PyObject* self, PyObject *args)
 {
     int signum, err;
 
-    if (PyArg_ParseTuple(args, "i:raise_signal", &signum) < 0)
+    if (!PyArg_ParseTuple(args, "i:raise_signal", &signum)) {
         return NULL;
+    }
 
     err = raise(signum);
     if (err)
@@ -4148,6 +4166,20 @@ raise_SIGINT_then_send_None(PyObject *self, PyObject *args)
 }
 
 
+#ifdef W_STOPCODE
+static PyObject*
+py_w_stopcode(PyObject *self, PyObject *args)
+{
+    int sig, status;
+    if (!PyArg_ParseTuple(args, "i", &sig)) {
+        return NULL;
+    }
+    status = W_STOPCODE(sig);
+    return PyLong_FromLong(status);
+}
+#endif
+
+
 static PyMethodDef TestMethods[] = {
     {"raise_exception",         raise_exception,                 METH_VARARGS},
     {"raise_memoryerror",   (PyCFunction)raise_memoryerror,  METH_NOARGS},
@@ -4355,6 +4387,9 @@ static PyMethodDef TestMethods[] = {
     {"pyobject_fastcalldict", test_pyobject_fastcalldict, METH_VARARGS},
     {"pyobject_fastcallkeywords", test_pyobject_fastcallkeywords, METH_VARARGS},
     {"raise_SIGINT_then_send_None", raise_SIGINT_then_send_None, METH_VARARGS},
+#ifdef W_STOPCODE
+    {"W_STOPCODE", py_w_stopcode, METH_VARARGS},
+#endif
     {NULL, NULL} /* sentinel */
 };
 
index 3e96aa0..4a1324c 100644 (file)
@@ -3615,7 +3615,7 @@ PyInit__tkinter(void)
                     return NULL;
                 }
                 if (str_path != NULL) {
-                    wcs_path = PyUnicode_AsWideCharString(str_path, NULL);
+                    wcs_path = _PyUnicode_AsWideCharString(str_path);
                     if (wcs_path == NULL) {
                         return NULL;
                     }
index ec8bd96..4f3bb54 100644 (file)
@@ -77,7 +77,7 @@ typedef struct
 #ifdef __GNUC__
 __attribute__((packed))
 #elif defined(_MSC_VER)
-_declspec(align(4))
+#pragma pack(push, 4)
 #endif
 {
     /* filename cannot be NULL: "<unknown>" is used if the Python frame
@@ -85,6 +85,9 @@ _declspec(align(4))
     PyObject *filename;
     unsigned int lineno;
 } frame_t;
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif
 
 
 typedef struct {
@@ -1731,7 +1734,7 @@ _PyTraceMalloc_Init(void)
         if (key == NULL)
             return -1;
 
-        value = PyDict_GetItemWithError(xoptions, key);
+        value = PyDict_GetItemWithError(xoptions, key); /* borrowed */
         Py_DECREF(key);
         if (value == NULL) {
             if (PyErr_Occurred())
@@ -1742,7 +1745,6 @@ _PyTraceMalloc_Init(void)
         }
 
         nframe = parse_sys_xoptions(value);
-        Py_DECREF(value);
         if (nframe < 0) {
             Py_FatalError("-X tracemalloc=NFRAME: invalid number of frames");
         }
index 1606f0d..b98e778 100644 (file)
@@ -723,9 +723,13 @@ getenvironment(PyObject* environment)
     }
 
     keys = PyMapping_Keys(environment);
+    if (!keys) {
+        return NULL;
+    }
     values = PyMapping_Values(environment);
-    if (!keys || !values)
+    if (!values) {
         goto error;
+    }
 
     envsize = PySequence_Fast_GET_SIZE(keys);
     if (PySequence_Fast_GET_SIZE(values) != envsize) {
index dcd7788..d582042 100644 (file)
@@ -1293,7 +1293,7 @@ audioop_ratecv_impl(PyObject *module, Py_buffer *fragment, int width,
     char *cp, *ncp;
     Py_ssize_t len;
     int chan, d, *prev_i, *cur_i, cur_o;
-    PyObject *samps, *str, *rv = NULL;
+    PyObject *samps, *str, *rv = NULL, *channel;
     int bytes_per_frame;
 
     if (!audioop_check_size(width))
@@ -1354,6 +1354,10 @@ audioop_ratecv_impl(PyObject *module, Py_buffer *fragment, int width,
             prev_i[chan] = cur_i[chan] = 0;
     }
     else {
+        if (!PyTuple_Check(state)) {
+            PyErr_SetString(PyExc_TypeError, "state must be a tuple or None");
+            goto exit;
+        }
         if (!PyArg_ParseTuple(state,
                         "iO!;audioop.ratecv: illegal state argument",
                         &d, &PyTuple_Type, &samps))
@@ -1364,7 +1368,13 @@ audioop_ratecv_impl(PyObject *module, Py_buffer *fragment, int width,
             goto exit;
         }
         for (chan = 0; chan < nchannels; chan++) {
-            if (!PyArg_ParseTuple(PyTuple_GetItem(samps, chan),
+            channel = PyTuple_GetItem(samps, chan);
+            if (!PyTuple_Check(channel)) {
+                PyErr_SetString(PyExc_TypeError,
+                                "ratecv(): illegal state argument");
+                goto exit;
+            }
+            if (!PyArg_ParseTuple(channel,
                                   "ii:ratecv", &prev_i[chan],
                                                &cur_i[chan]))
                 goto exit;
index 1ce4218..7394cf6 100644 (file)
@@ -807,15 +807,9 @@ jisx0213_encoder(const Py_UCS4 *data, Py_ssize_t *length, void *config)
     case 2: /* second character of unicode pair */
         coded = find_pairencmap((ucs2_t)data[0], (ucs2_t)data[1],
                                 jisx0213_pair_encmap, JISX0213_ENCPAIRS);
-        if (coded == DBCINV) {
-            *length = 1;
-            coded = find_pairencmap((ucs2_t)data[0], 0,
-                      jisx0213_pair_encmap, JISX0213_ENCPAIRS);
-            if (coded == DBCINV)
-                return MAP_UNMAPPABLE;
-        }
-        else
+        if (coded != DBCINV)
             return coded;
+        /* fall through */
 
     case -1: /* flush unterminated */
         *length = 1;
index 41f11f4..62bf8e7 100644 (file)
@@ -28,7 +28,7 @@ _asyncio_Future___init__(PyObject *self, PyObject *args, PyObject *kwargs)
     int return_value = -1;
     static const char * const _keywords[] = {"loop", NULL};
     static _PyArg_Parser _parser = {"|$O:Future", _keywords, 0};
-    PyObject *loop = NULL;
+    PyObject *loop = Py_None;
 
     if (!_PyArg_ParseTupleAndKeywordsFast(args, kwargs, &_parser,
         &loop)) {
@@ -244,7 +244,7 @@ _asyncio_Task___init__(PyObject *self, PyObject *args, PyObject *kwargs)
     static const char * const _keywords[] = {"coro", "loop", NULL};
     static _PyArg_Parser _parser = {"O|$O:Task", _keywords, 0};
     PyObject *coro;
-    PyObject *loop = NULL;
+    PyObject *loop = Py_None;
 
     if (!_PyArg_ParseTupleAndKeywordsFast(args, kwargs, &_parser,
         &coro, &loop)) {
@@ -477,7 +477,7 @@ _asyncio_Task__step(TaskObj *self, PyObject **args, Py_ssize_t nargs, PyObject *
     PyObject *return_value = NULL;
     static const char * const _keywords[] = {"exc", NULL};
     static _PyArg_Parser _parser = {"|O:_step", _keywords, 0};
-    PyObject *exc = NULL;
+    PyObject *exc = Py_None;
 
     if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser,
         &exc)) {
@@ -517,4 +517,4 @@ _asyncio_Task__wakeup(TaskObj *self, PyObject **args, Py_ssize_t nargs, PyObject
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=40ca6c9da517da73 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=7441872b13652085 input=a9049054013a1b77]*/
index 75f8f5a..6f74890 100644 (file)
@@ -132,7 +132,7 @@ _ssl__SSLSocket_version(PySSLSocket *self, PyObject *Py_UNUSED(ignored))
     return _ssl__SSLSocket_version_impl(self);
 }
 
-#if defined(OPENSSL_NPN_NEGOTIATED)
+#if (defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG))
 
 PyDoc_STRVAR(_ssl__SSLSocket_selected_npn_protocol__doc__,
 "selected_npn_protocol($self, /)\n"
@@ -151,7 +151,7 @@ _ssl__SSLSocket_selected_npn_protocol(PySSLSocket *self, PyObject *Py_UNUSED(ign
     return _ssl__SSLSocket_selected_npn_protocol_impl(self);
 }
 
-#endif /* defined(OPENSSL_NPN_NEGOTIATED) */
+#endif /* (defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)) */
 
 #if defined(HAVE_ALPN)
 
@@ -1168,4 +1168,4 @@ exit:
 #ifndef _SSL_ENUM_CRLS_METHODDEF
     #define _SSL_ENUM_CRLS_METHODDEF
 #endif /* !defined(_SSL_ENUM_CRLS_METHODDEF) */
-/*[clinic end generated code: output=56cead8610faa505 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=a8b184655068c238 input=a9049054013a1b77]*/
index d10530b..c3587e5 100644 (file)
@@ -1,5 +1,33 @@
-/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
-   See the file COPYING for copying permission.
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000-2017 Expat development team
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
 #define ASCII_A 0x41
index 79a15c2..2f59fd9 100644 (file)
@@ -1,5 +1,33 @@
-/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
-   See the file COPYING for copying permission.
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000-2017 Expat development team
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
 /* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
index 28b0f95..d0735bb 100644 (file)
@@ -1,5 +1,33 @@
-/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
-   See the file COPYING for copying permission.
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000-2017 Expat development team
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
 #ifndef Expat_INCLUDED
@@ -24,7 +52,6 @@ extern "C" {
 struct XML_ParserStruct;
 typedef struct XML_ParserStruct *XML_Parser;
 
-/* Should this be defined using stdbool.h when C99 is available? */
 typedef unsigned char XML_Bool;
 #define XML_TRUE   ((XML_Bool) 1)
 #define XML_FALSE  ((XML_Bool) 0)
@@ -1049,7 +1076,7 @@ XML_GetFeatureList(void);
 */
 #define XML_MAJOR_VERSION 2
 #define XML_MINOR_VERSION 2
-#define XML_MICRO_VERSION 1
+#define XML_MICRO_VERSION 4
 
 #ifdef __cplusplus
 }
index 4c9e5ea..8110285 100644 (file)
@@ -1,5 +1,33 @@
-/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
-   See the file COPYING for copying permission.
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000-2017 Expat development team
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
 #ifndef Expat_External_INCLUDED
index 24a1d5c..ce4a4bf 100644 (file)
@@ -1,5 +1,33 @@
-/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
-   See the file COPYING for copying permission.
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000-2017 Expat development team
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
 /* Like asciitab.h, except that 0xD has code BT_S rather than BT_CR */
index 94cb98e..3c5d6e9 100644 (file)
 
    Note: Use of these macros is based on judgement, not hard rules,
          and therefore subject to change.
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000-2017 Expat development team
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
 #if defined(__GNUC__) && defined(__i386__) && !defined(__MINGW32__)
index 53c25d7..95dfa52 100644 (file)
@@ -1,5 +1,33 @@
-/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
-   See the file COPYING for copying permission.
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000-2017 Expat development team
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
 /* 0x80 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
diff --git a/Modules/expat/loadlibrary.c b/Modules/expat/loadlibrary.c
new file mode 100644 (file)
index 0000000..452ae92
--- /dev/null
@@ -0,0 +1,143 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2016 - 2017, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) 2017, Expat development team
+ *
+ * All rights reserved.
+ * Licensed under the MIT license:
+ *
+ * Permission to  use, copy,  modify, and distribute  this software  for any
+ * purpose with  or without fee is  hereby granted, provided that  the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+ * EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+ * MERCHANTABILITY, FITNESS FOR A  PARTICULAR PURPOSE AND NONINFRINGEMENT OF
+ * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR  OTHERWISE, ARISING FROM, OUT OF OR  IN CONNECTION WITH
+ * THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice,  the name of a copyright holder shall
+ * not be used in advertising or otherwise to promote the sale, use or other
+ * dealings  in this  Software without  prior written  authorization of  the
+ * copyright holder.
+ *
+ ***************************************************************************/
+
+#if defined(_WIN32)
+
+#include <windows.h>
+#include <tchar.h>
+
+
+HMODULE _Expat_LoadLibrary(LPCTSTR filename);
+
+
+#if !defined(LOAD_WITH_ALTERED_SEARCH_PATH)
+#define LOAD_WITH_ALTERED_SEARCH_PATH  0x00000008
+#endif
+
+#if !defined(LOAD_LIBRARY_SEARCH_SYSTEM32)
+#define LOAD_LIBRARY_SEARCH_SYSTEM32   0x00000800
+#endif
+
+/* We use our own typedef here since some headers might lack these */
+typedef HMODULE (APIENTRY *LOADLIBRARYEX_FN)(LPCTSTR, HANDLE, DWORD);
+
+/* See function definitions in winbase.h */
+#ifdef UNICODE
+#  ifdef _WIN32_WCE
+#    define LOADLIBARYEX  L"LoadLibraryExW"
+#  else
+#    define LOADLIBARYEX  "LoadLibraryExW"
+#  endif
+#else
+#  define LOADLIBARYEX    "LoadLibraryExA"
+#endif
+
+
+/*
+ * _Expat_LoadLibrary()
+ *
+ * This is used to dynamically load DLLs using the most secure method available
+ * for the version of Windows that we are running on.
+ *
+ * Parameters:
+ *
+ * filename  [in] - The filename or full path of the DLL to load. If only the
+ *                  filename is passed then the DLL will be loaded from the
+ *                  Windows system directory.
+ *
+ * Returns the handle of the module on success; otherwise NULL.
+ */
+HMODULE _Expat_LoadLibrary(LPCTSTR filename)
+{
+  HMODULE hModule = NULL;
+  LOADLIBRARYEX_FN pLoadLibraryEx = NULL;
+
+  /* Get a handle to kernel32 so we can access it's functions at runtime */
+  HMODULE hKernel32 = GetModuleHandle(TEXT("kernel32"));
+  if(!hKernel32)
+    return NULL;
+
+  /* Attempt to find LoadLibraryEx() which is only available on Windows 2000
+     and above */
+  pLoadLibraryEx = (LOADLIBRARYEX_FN) GetProcAddress(hKernel32, LOADLIBARYEX);
+
+  /* Detect if there's already a path in the filename and load the library if
+     there is. Note: Both back slashes and forward slashes have been supported
+     since the earlier days of DOS at an API level although they are not
+     supported by command prompt */
+  if(_tcspbrk(filename, TEXT("\\/"))) {
+    /** !checksrc! disable BANNEDFUNC 1 **/
+    hModule = pLoadLibraryEx ?
+      pLoadLibraryEx(filename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) :
+      LoadLibrary(filename);
+  }
+  /* Detect if KB2533623 is installed, as LOAD_LIBARY_SEARCH_SYSTEM32 is only
+     supported on Windows Vista, Windows Server 2008, Windows 7 and Windows
+     Server 2008 R2 with this patch or natively on Windows 8 and above */
+  else if(pLoadLibraryEx && GetProcAddress(hKernel32, "AddDllDirectory")) {
+    /* Load the DLL from the Windows system directory */
+    hModule = pLoadLibraryEx(filename, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
+  }
+  else {
+    /* Attempt to get the Windows system path */
+    UINT systemdirlen = GetSystemDirectory(NULL, 0);
+    if(systemdirlen) {
+      /* Allocate space for the full DLL path (Room for the null terminator
+         is included in systemdirlen) */
+      size_t filenamelen = _tcslen(filename);
+      TCHAR *path = malloc(sizeof(TCHAR) * (systemdirlen + 1 + filenamelen));
+      if(path && GetSystemDirectory(path, systemdirlen)) {
+        /* Calculate the full DLL path */
+        _tcscpy(path + _tcslen(path), TEXT("\\"));
+        _tcscpy(path + _tcslen(path), filename);
+
+        /* Load the DLL from the Windows system directory */
+        /** !checksrc! disable BANNEDFUNC 1 **/
+        hModule = pLoadLibraryEx ?
+          pLoadLibraryEx(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) :
+          LoadLibrary(path);
+
+      }
+      free(path);
+    }
+  }
+
+  return hModule;
+}
+
+#else /* defined(_WIN32) */
+
+/* ISO C requires a translation unit to contain at least one declaration
+   [-Wempty-translation-unit] */
+typedef int _TRANSLATION_UNIT_LOAD_LIBRARY_C_NOT_EMTPY;
+
+#endif /* defined(_WIN32) */
index b05e62c..bfa2bd3 100644 (file)
@@ -1,3 +1,35 @@
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000-2017 Expat development team
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
 static const unsigned namingBitmap[] = {
 0x00000000, 0x00000000, 0x00000000, 0x00000000,
 0x00000000, 0x00000000, 0x00000000, 0x00000000,
index 23b56d2..581872d 100644 (file)
@@ -2,9 +2,8 @@
  * siphash.h - SipHash-2-4 in a single header file
  * --------------------------------------------------------------------------
  * Derived by William Ahern from the reference implementation[1] published[2]
- * by Jean-Philippe Aumasson and Daniel J. Berstein. Licensed in kind.
  * by Jean-Philippe Aumasson and Daniel J. Berstein.
- * Minimal changes by Sebastian Pipping on top, details below.
+ * Minimal changes by Sebastian Pipping and Victor Stinner on top, see below.
  * Licensed under the CC0 Public Domain Dedication license.
  *
  * 1. https://www.131002.net/siphash/siphash24.c
  * --------------------------------------------------------------------------
  * HISTORY:
  *
- * 2017-06-10  (Sebastian Pipping)
+ * 2017-07-25  (Vadim Zeitlin)
+ *   - Fix use of SIPHASH_MAIN macro
+ *
+ * 2017-07-05  (Sebastian Pipping)
+ *   - Use _SIP_ULL macro to not require a C++11 compiler if compiled as C++
+ *   - Add const qualifiers at two places
+ *   - Ensure <=80 characters line length (assuming tab width 4)
+ *
+ * 2017-06-23  (Victor Stinner)
+ *   - Address Win64 compile warnings
+ *
+ * 2017-06-18  (Sebastian Pipping)
  *   - Clarify license note in the header
  *   - Address C89 issues:
  *     - Stop using inline keyword (and let compiler decide)
- *     - Turn integer suffix ULL to UL
  *     - Replace _Bool by int
  *     - Turn macro siphash24 into a function
  *     - Address invalid conversion (void pointer) by explicit cast
+ *   - Address lack of stdint.h for Visual Studio 2003 to 2008
  *   - Always expose sip24_valid (for self-tests)
  *
  * 2012-11-04 - Born.  (William Ahern)
 #define SIPHASH_H
 
 #include <stddef.h> /* size_t */
-#include <stdint.h> /* uint64_t uint32_t uint8_t */
+
+#if defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER < 1600)
+  /* For vs2003/7.1 up to vs2008/9.0; _MSC_VER 1600 is vs2010/10.0 */
+  typedef unsigned __int8   uint8_t;
+  typedef unsigned __int32 uint32_t;
+  typedef unsigned __int64 uint64_t;
+#else
+ #include <stdint.h> /* uint64_t uint32_t uint8_t */
+#endif
+
+
+/*
+ * Workaround to not require a C++11 compiler for using ULL suffix
+ * if this code is included and compiled as C++; related GCC warning is:
+ * warning: use of C++11 long long integer constant [-Wlong-long]
+ */
+#define _SIP_ULL(high, low)  (((uint64_t)high << 32) | low)
 
 
 #define SIP_ROTL(x, b) (uint64_t)(((x) << (b)) | ( (x) >> (64 - (b))))
@@ -158,11 +184,12 @@ static void sip_round(struct siphash *H, const int rounds) {
 } /* sip_round() */
 
 
-static struct siphash *sip24_init(struct siphash *H, const struct sipkey *key) {
-       H->v0 = 0x736f6d6570736575UL ^ key->k[0];
-       H->v1 = 0x646f72616e646f6dUL ^ key->k[1];
-       H->v2 = 0x6c7967656e657261UL ^ key->k[0];
-       H->v3 = 0x7465646279746573UL ^ key->k[1];
+static struct siphash *sip24_init(struct siphash *H,
+               const struct sipkey *key) {
+       H->v0 = _SIP_ULL(0x736f6d65U, 0x70736575U) ^ key->k[0];
+       H->v1 = _SIP_ULL(0x646f7261U, 0x6e646f6dU) ^ key->k[1];
+       H->v2 = _SIP_ULL(0x6c796765U, 0x6e657261U) ^ key->k[0];
+       H->v3 = _SIP_ULL(0x74656462U, 0x79746573U) ^ key->k[1];
 
        H->p = H->buf;
        H->c = 0;
@@ -173,7 +200,8 @@ static struct siphash *sip24_init(struct siphash *H, const struct sipkey *key) {
 
 #define sip_endof(a) (&(a)[sizeof (a) / sizeof *(a)])
 
-static struct siphash *sip24_update(struct siphash *H, const void *src, size_t len) {
+static struct siphash *sip24_update(struct siphash *H, const void *src,
+               size_t len) {
        const unsigned char *p = (const unsigned char *)src, *pe = p + len;
        uint64_t m;
 
@@ -198,7 +226,7 @@ static struct siphash *sip24_update(struct siphash *H, const void *src, size_t l
 
 
 static uint64_t sip24_final(struct siphash *H) {
-       char left = H->p - H->buf;
+       const char left = (char)(H->p - H->buf);
        uint64_t b = (H->c + left) << 56;
 
        switch (left) {
@@ -222,7 +250,8 @@ static uint64_t sip24_final(struct siphash *H) {
 } /* sip24_final() */
 
 
-static uint64_t siphash24(const void *src, size_t len, const struct sipkey *key) {
+static uint64_t siphash24(const void *src, size_t len,
+               const struct sipkey *key) {
        struct siphash state = SIPHASH_INITIALIZER;
        return sip24_final(sip24_update(sip24_init(&state, key), src, len));
 } /* siphash24() */
@@ -310,10 +339,11 @@ static int sip24_valid(void) {
        struct sipkey k;
        size_t i;
 
-       sip_tokey(&k, "\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017");
+       sip_tokey(&k, "\000\001\002\003\004\005\006\007\010\011"
+                       "\012\013\014\015\016\017");
 
        for (i = 0; i < sizeof in; ++i) {
-               in[i] = i;
+               in[i] = (unsigned char)i;
 
                if (siphash24(in, i, &k) != SIP_U8TO64_LE(vectors[i]))
                        return 0;
@@ -323,12 +353,12 @@ static int sip24_valid(void) {
 } /* sip24_valid() */
 
 
-#if SIPHASH_MAIN
+#ifdef SIPHASH_MAIN
 
 #include <stdio.h>
 
 int main(void) {
-       int ok = sip24_valid();
+       const int ok = sip24_valid();
 
        if (ok)
                puts("OK");
index 7bb3e77..fa0bed6 100644 (file)
@@ -1,7 +1,34 @@
-/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
-   See the file COPYING for copying permission.
-*/
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000-2017 Expat development team
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
 
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
 
 /* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
 /* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
index 9bf014d..17fea46 100644 (file)
@@ -1,10 +1,33 @@
-/*================================================================
-** Copyright 2000, Clark Cooper
-** All rights reserved.
-**
-** This is free software. You are permitted to copy, distribute, or modify
-** it under the terms of the MIT/X license (contained in the COPYING file
-** with this distribution.)
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000-2017 Expat development team
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
 #ifndef WINCONFIG_H
index daec151..0df6883 100644 (file)
@@ -1,10 +1,38 @@
-/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
-   See the file COPYING for copying permission.
-
-   77fea421d361dca90041d0040ecf1dca651167fadf2af79e990e35168d70d933 (2.2.1+)
+/* 8c6b2be7c6281da65ce05218fc15c339f02a811706340824ab596aa86e1fd51a (2.2.4+)
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000-2017 Expat development team
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
-#define _GNU_SOURCE 1                   /* syscall prototype */
+#if !defined(_GNU_SOURCE)
+# define _GNU_SOURCE 1                  /* syscall prototype */
+#endif
 
 #include <stddef.h>
 #include <string.h>                     /* memset(), memcpy() */
@@ -19,6 +47,8 @@
 #include <sys/time.h>                   /* gettimeofday() */
 #include <sys/types.h>                  /* getpid() */
 #include <unistd.h>                     /* getpid() */
+#include <fcntl.h>                      /* O_RDONLY */
+#include <errno.h>
 #endif
 
 #define XML_BUILDING_EXPAT 1
 #include "expat.h"
 #include "siphash.h"
 
+#if defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM)
+# if defined(HAVE_GETRANDOM)
+#  include <sys/random.h>    /* getrandom */
+# else
+#  include <unistd.h>        /* syscall */
+#  include <sys/syscall.h>   /* SYS_getrandom */
+# endif
+# if ! defined(GRND_NONBLOCK)
+#  define GRND_NONBLOCK  0x0001
+# endif  /* defined(GRND_NONBLOCK) */
+#endif  /* defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM) */
+
+#if defined(HAVE_LIBBSD) \
+    && (defined(HAVE_ARC4RANDOM_BUF) || defined(HAVE_ARC4RANDOM))
+# include <bsd/stdlib.h>
+#endif
+
+#if defined(_WIN32) && !defined(LOAD_LIBRARY_SEARCH_SYSTEM32)
+# define LOAD_LIBRARY_SEARCH_SYSTEM32  0x00000800
+#endif
+
+#if !defined(HAVE_GETRANDOM) && !defined(HAVE_SYSCALL_GETRANDOM) \
+    && !defined(HAVE_ARC4RANDOM_BUF) && !defined(HAVE_ARC4RANDOM) \
+    && !defined(XML_DEV_URANDOM) \
+    && !defined(_WIN32) \
+    && !defined(XML_POOR_ENTROPY)
+# error  \
+    You do not have support for any sources of high quality entropy \
+    enabled.  For end user security, that is probably not what you want. \
+    \
+    Your options include: \
+      * Linux + glibc >=2.25 (getrandom): HAVE_GETRANDOM, \
+      * Linux + glibc <2.25 (syscall SYS_getrandom): HAVE_SYSCALL_GETRANDOM, \
+      * BSD / macOS >=10.7 (arc4random_buf): HAVE_ARC4RANDOM_BUF, \
+      * BSD / macOS <10.7 (arc4random): HAVE_ARC4RANDOM, \
+      * libbsd (arc4random_buf): HAVE_ARC4RANDOM_BUF + HAVE_LIBBSD, \
+      * libbsd (arc4random): HAVE_ARC4RANDOM + HAVE_LIBBSD, \
+      * Linux / BSD / macOS (/dev/urandom): XML_DEV_URANDOM \
+      * Windows (RtlGenRandom): _WIN32. \
+    \
+    If insist on not using any of these, bypass this error by defining \
+    XML_POOR_ENTROPY; you have been warned. \
+    \
+    If you have reasons to patch this detection code away or need changes \
+    to the build system, please open a bug.  Thank you!
+#endif
+
+
 #ifdef XML_UNICODE
 #define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX
 #define XmlConvert XmlUtf16Convert
@@ -436,6 +514,9 @@ static ELEMENT_TYPE *
 getElementType(XML_Parser parser, const ENCODING *enc,
                const char *ptr, const char *end);
 
+static XML_Char *copyString(const XML_Char *s,
+                            const XML_Memory_Handling_Suite *memsuite);
+
 static unsigned long generate_hash_secret_salt(XML_Parser parser);
 static XML_Bool startParsing(XML_Parser parser);
 
@@ -696,21 +777,13 @@ static const XML_Char implicitContext[] = {
 
 
 #if defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM)
-# include <errno.h>
-
-# if defined(HAVE_GETRANDOM)
-#  include <sys/random.h>    /* getrandom */
-# else
-#  include <unistd.h>        /* syscall */
-#  include <sys/syscall.h>   /* SYS_getrandom */
-# endif
 
 /* Obtain entropy on Linux 3.17+ */
 static int
-writeRandomBytes_getrandom(void * target, size_t count) {
+writeRandomBytes_getrandom_nonblock(void * target, size_t count) {
   int success = 0;  /* full count bytes written? */
   size_t bytesWrittenTotal = 0;
-  const unsigned int getrandomFlags = 0;
+  const unsigned int getrandomFlags = GRND_NONBLOCK;
 
   do {
     void * const currentTarget = (void*)((char*)target + bytesWrittenTotal);
@@ -728,7 +801,7 @@ writeRandomBytes_getrandom(void * target, size_t count) {
       if (bytesWrittenTotal >= count)
         success = 1;
     }
-  } while (! success && (errno == EINTR || errno == EAGAIN));
+  } while (! success && (errno == EINTR));
 
   return success;
 }
@@ -736,12 +809,67 @@ writeRandomBytes_getrandom(void * target, size_t count) {
 #endif  /* defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM) */
 
 
+#if ! defined(_WIN32) && defined(XML_DEV_URANDOM)
+
+/* Extract entropy from /dev/urandom */
+static int
+writeRandomBytes_dev_urandom(void * target, size_t count) {
+  int success = 0;  /* full count bytes written? */
+  size_t bytesWrittenTotal = 0;
+
+  const int fd = open("/dev/urandom", O_RDONLY);
+  if (fd < 0) {
+    return 0;
+  }
+
+  do {
+    void * const currentTarget = (void*)((char*)target + bytesWrittenTotal);
+    const size_t bytesToWrite = count - bytesWrittenTotal;
+
+    const ssize_t bytesWrittenMore = read(fd, currentTarget, bytesToWrite);
+
+    if (bytesWrittenMore > 0) {
+      bytesWrittenTotal += bytesWrittenMore;
+      if (bytesWrittenTotal >= count)
+        success = 1;
+    }
+  } while (! success && (errno == EINTR));
+
+  close(fd);
+  return success;
+}
+
+#endif  /* ! defined(_WIN32) && defined(XML_DEV_URANDOM) */
+
+
+#if defined(HAVE_ARC4RANDOM)
+
+static void
+writeRandomBytes_arc4random(void * target, size_t count) {
+  size_t bytesWrittenTotal = 0;
+
+  while (bytesWrittenTotal < count) {
+    const uint32_t random32 = arc4random();
+    size_t i = 0;
+
+    for (; (i < sizeof(random32)) && (bytesWrittenTotal < count);
+        i++, bytesWrittenTotal++) {
+      const uint8_t random8 = (uint8_t)(random32 >> (i * 8));
+      ((uint8_t *)target)[bytesWrittenTotal] = random8;
+    }
+  }
+}
+
+#endif  /* defined(HAVE_ARC4RANDOM) */
+
+
 #ifdef _WIN32
 
 typedef BOOLEAN (APIENTRY *RTLGENRANDOM_FUNC)(PVOID, ULONG);
+HMODULE _Expat_LoadLibrary(LPCTSTR filename);  /* see loadlibrary.c */
 
 /* Obtain entropy on Windows XP / Windows Server 2003 and later.
- * Hint on RtlGenRandom and the following article from libsodioum.
+ * Hint on RtlGenRandom and the following article from libsodium.
  *
  * Michael Howard: Cryptographically Secure Random number on Windows without using CryptoAPI
  * https://blogs.msdn.microsoft.com/michael_howard/2005/01/14/cryptographically-secure-random-number-on-windows-without-using-cryptoapi/
@@ -749,7 +877,7 @@ typedef BOOLEAN (APIENTRY *RTLGENRANDOM_FUNC)(PVOID, ULONG);
 static int
 writeRandomBytes_RtlGenRandom(void * target, size_t count) {
   int success = 0;  /* full count bytes written? */
-  const HMODULE advapi32 = LoadLibrary("ADVAPI32.DLL");
+  const HMODULE advapi32 = _Expat_LoadLibrary(TEXT("ADVAPI32.DLL"));
 
   if (advapi32) {
     const RTLGENRANDOM_FUNC RtlGenRandom
@@ -768,6 +896,8 @@ writeRandomBytes_RtlGenRandom(void * target, size_t count) {
 #endif /* _WIN32 */
 
 
+#if ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM)
+
 static unsigned long
 gather_time_entropy(void)
 {
@@ -780,16 +910,20 @@ gather_time_entropy(void)
   int gettimeofday_res;
 
   gettimeofday_res = gettimeofday(&tv, NULL);
+
+#if defined(NDEBUG)
+  (void)gettimeofday_res;
+#else
   assert (gettimeofday_res == 0);
+#endif  /* defined(NDEBUG) */
 
   /* Microseconds time is <20 bits entropy */
   return tv.tv_usec;
 #endif
 }
 
-#if defined(HAVE_ARC4RANDOM_BUF) && defined(HAVE_LIBBSD)
-# include <bsd/stdlib.h>
-#endif
+#endif  /* ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM) */
+
 
 static unsigned long
 ENTROPY_DEBUG(const char * label, unsigned long entropy) {
@@ -808,10 +942,12 @@ generate_hash_secret_salt(XML_Parser parser)
 {
   unsigned long entropy;
   (void)parser;
-#if defined(HAVE_ARC4RANDOM_BUF) || defined(__CloudABI__)
-  (void)gather_time_entropy;
+#if defined(HAVE_ARC4RANDOM_BUF)
   arc4random_buf(&entropy, sizeof(entropy));
   return ENTROPY_DEBUG("arc4random_buf", entropy);
+#elif defined(HAVE_ARC4RANDOM)
+  writeRandomBytes_arc4random((void *)&entropy, sizeof(entropy));
+  return ENTROPY_DEBUG("arc4random", entropy);
 #else
   /* Try high quality providers first .. */
 #ifdef _WIN32
@@ -819,10 +955,15 @@ generate_hash_secret_salt(XML_Parser parser)
     return ENTROPY_DEBUG("RtlGenRandom", entropy);
   }
 #elif defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM)
-  if (writeRandomBytes_getrandom((void *)&entropy, sizeof(entropy))) {
+  if (writeRandomBytes_getrandom_nonblock((void *)&entropy, sizeof(entropy))) {
     return ENTROPY_DEBUG("getrandom", entropy);
   }
 #endif
+#if ! defined(_WIN32) && defined(XML_DEV_URANDOM)
+  if (writeRandomBytes_dev_urandom((void *)&entropy, sizeof(entropy))) {
+    return ENTROPY_DEBUG("/dev/urandom", entropy);
+  }
+#endif  /* ! defined(_WIN32) && defined(XML_DEV_URANDOM) */
   /* .. and self-made low quality for backup: */
 
   /* Process ID is 0 bits entropy if attacker has local access */
@@ -833,7 +974,7 @@ generate_hash_secret_salt(XML_Parser parser)
     return ENTROPY_DEBUG("fallback(4)", entropy * 2147483647);
   } else {
     return ENTROPY_DEBUG("fallback(8)",
-        entropy * (unsigned long)2305843009213693951);
+        entropy * (unsigned long)2305843009213693951ULL);
   }
 #endif
 }
@@ -962,6 +1103,8 @@ parserCreate(const XML_Char *encodingName,
   nsAttsVersion = 0;
   nsAttsPower = 0;
 
+  protocolEncodingName = NULL;
+
   poolInit(&tempPool, &(parser->m_mem));
   poolInit(&temp2Pool, &(parser->m_mem));
   parserInit(parser, encodingName);
@@ -988,9 +1131,9 @@ parserInit(XML_Parser parser, const XML_Char *encodingName)
 {
   processor = prologInitProcessor;
   XmlPrologStateInit(&prologState);
-  protocolEncodingName = (encodingName != NULL
-                          ? poolCopyString(&tempPool, encodingName)
-                          : NULL);
+  if (encodingName != NULL) {
+    protocolEncodingName = copyString(encodingName, &(parser->m_mem));
+  }
   curBase = NULL;
   XmlInitEncoding(&initEncoding, &encoding, 0);
   userData = NULL;
@@ -1103,6 +1246,8 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName)
     unknownEncodingRelease(unknownEncodingData);
   poolClear(&tempPool);
   poolClear(&temp2Pool);
+  FREE((void *)protocolEncodingName);
+  protocolEncodingName = NULL;
   parserInit(parser, encodingName);
   dtdReset(_dtd, &parser->m_mem);
   return XML_TRUE;
@@ -1119,10 +1264,16 @@ XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName)
   */
   if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED)
     return XML_STATUS_ERROR;
+
+  /* Get rid of any previous encoding name */
+  FREE((void *)protocolEncodingName);
+
   if (encodingName == NULL)
+    /* No new encoding name */
     protocolEncodingName = NULL;
   else {
-    protocolEncodingName = poolCopyString(&tempPool, encodingName);
+    /* Copy the new encoding name into allocated memory */
+    protocolEncodingName = copyString(encodingName, &(parser->m_mem));
     if (!protocolEncodingName)
       return XML_STATUS_ERROR;
   }
@@ -1357,6 +1508,7 @@ XML_ParserFree(XML_Parser parser)
   destroyBindings(inheritedBindings, parser);
   poolDestroy(&tempPool);
   poolDestroy(&temp2Pool);
+  FREE((void *)protocolEncodingName);
 #ifdef XML_DTD
   /* external parameter entity parsers share the DTD structure
      parser->m_dtd with the root parser, so we must not destroy it
@@ -1748,7 +1900,8 @@ enum XML_Status XMLCALL
 XML_Parse(XML_Parser parser, const char *s, int len, int isFinal)
 {
   if ((parser == NULL) || (len < 0) || ((s == NULL) && (len != 0))) {
-    errorCode = XML_ERROR_INVALID_ARGUMENT;
+    if (parser != NULL)
+      parser->m_errorCode = XML_ERROR_INVALID_ARGUMENT;
     return XML_STATUS_ERROR;
   }
   switch (ps_parsing) {
@@ -1783,9 +1936,22 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal)
     if (errorCode == XML_ERROR_NONE) {
       switch (ps_parsing) {
       case XML_SUSPENDED:
+        /* It is hard to be certain, but it seems that this case
+         * cannot occur.  This code is cleaning up a previous parse
+         * with no new data (since len == 0).  Changing the parsing
+         * state requires getting to execute a handler function, and
+         * there doesn't seem to be an opportunity for that while in
+         * this circumstance.
+         *
+         * Given the uncertainty, we retain the code but exclude it
+         * from coverage tests.
+         *
+         * LCOV_EXCL_START
+         */
         XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position);
         positionPtr = bufferPtr;
         return XML_STATUS_SUSPENDED;
+        /* LCOV_EXCL_STOP */
       case XML_INITIALIZED:
       case XML_PARSING:
         ps_parsing = XML_FINISHED;
@@ -2974,9 +3140,17 @@ doContent(XML_Parser parser,
         return XML_ERROR_NO_MEMORY;
       break;
     default:
+      /* All of the tokens produced by XmlContentTok() have their own
+       * explicit cases, so this default is not strictly necessary.
+       * However it is a useful safety net, so we retain the code and
+       * simply exclude it from the coverage tests.
+       *
+       * LCOV_EXCL_START
+       */
       if (defaultHandler)
         reportDefault(parser, enc, s, next);
       break;
+      /* LCOV_EXCL_STOP */
     }
     *eventPP = s = next;
     switch (ps_parsing) {
@@ -3067,13 +3241,17 @@ storeAtts(XML_Parser parser, const ENCODING *enc,
 #endif
     attsSize = n + nDefaultAtts + INIT_ATTS_SIZE;
     temp = (ATTRIBUTE *)REALLOC((void *)atts, attsSize * sizeof(ATTRIBUTE));
-    if (temp == NULL)
+    if (temp == NULL) {
+      attsSize = oldAttsSize;
       return XML_ERROR_NO_MEMORY;
+    }
     atts = temp;
 #ifdef XML_ATTR_INFO
     temp2 = (XML_AttrInfo *)REALLOC((void *)attInfo, attsSize * sizeof(XML_AttrInfo));
-    if (temp2 == NULL)
+    if (temp2 == NULL) {
+      attsSize = oldAttsSize;
       return XML_ERROR_NO_MEMORY;
+    }
     attInfo = temp2;
 #endif
     if (n > oldAttsSize)
@@ -3210,6 +3388,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc,
     int j;  /* hash table index */
     unsigned long version = nsAttsVersion;
     int nsAttsSize = (int)1 << nsAttsPower;
+    unsigned char oldNsAttsPower = nsAttsPower;
     /* size of hash table must be at least 2 * (# of prefixed attributes) */
     if ((nPrefixes << 1) >> nsAttsPower) {  /* true for nsAttsPower = 0 */
       NS_ATT *temp;
@@ -3219,8 +3398,11 @@ storeAtts(XML_Parser parser, const ENCODING *enc,
         nsAttsPower = 3;
       nsAttsSize = (int)1 << nsAttsPower;
       temp = (NS_ATT *)REALLOC(nsAtts, nsAttsSize * sizeof(NS_ATT));
-      if (!temp)
+      if (!temp) {
+        /* Restore actual size of memory in nsAtts */
+        nsAttsPower = oldNsAttsPower;
         return XML_ERROR_NO_MEMORY;
+      }
       nsAtts = temp;
       version = 0;  /* force re-initialization of nsAtts hash table */
     }
@@ -3247,8 +3429,23 @@ storeAtts(XML_Parser parser, const ENCODING *enc,
 
         ((XML_Char *)s)[-1] = 0;  /* clear flag */
         id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, s, 0);
-        if (!id || !id->prefix)
-          return XML_ERROR_NO_MEMORY;
+        if (!id || !id->prefix) {
+          /* This code is walking through the appAtts array, dealing
+           * with (in this case) a prefixed attribute name.  To be in
+           * the array, the attribute must have already been bound, so
+           * has to have passed through the hash table lookup once
+           * already.  That implies that an entry for it already
+           * exists, so the lookup above will return a pointer to
+           * already allocated memory.  There is no opportunaity for
+           * the allocator to fail, so the condition above cannot be
+           * fulfilled.
+           *
+           * Since it is difficult to be certain that the above
+           * analysis is complete, we retain the test and merely
+           * remove the code from coverage tests.
+           */
+          return XML_ERROR_NO_MEMORY; /* LCOV_EXCL_LINE */
+        }
         b = id->prefix->binding;
         if (!b)
           return XML_ERROR_UNBOUND_PREFIX;
@@ -3625,8 +3822,16 @@ doCdataSection(XML_Parser parser,
       }
       return XML_ERROR_UNCLOSED_CDATA_SECTION;
     default:
+      /* Every token returned by XmlCdataSectionTok() has its own
+       * explicit case, so this default case will never be executed.
+       * We retain it as a safety net and exclude it from the coverage
+       * statistics.
+       *
+       * LCOV_EXCL_START
+      */
       *eventPP = next;
       return XML_ERROR_UNEXPECTED_STATE;
+      /* LCOV_EXCL_STOP */
     }
 
     *eventPP = s = next;
@@ -3686,8 +3891,20 @@ doIgnoreSection(XML_Parser parser,
     eventEndPP = &eventEndPtr;
   }
   else {
+    /* It's not entirely clear, but it seems the following two lines
+     * of code cannot be executed.  The only occasions on which 'enc'
+     * is not 'parser->m_encoding' are when this function is called
+     * from the internal entity processing, and IGNORE sections are an
+     * error in internal entities.
+     *
+     * Since it really isn't clear that this is true, we keep the code
+     * and just remove it from our coverage tests.
+     *
+     * LCOV_EXCL_START
+     */
     eventPP = &(openInternalEntities->internalEventPtr);
     eventEndPP = &(openInternalEntities->internalEventEndPtr);
+    /* LCOV_EXCL_STOP */
   }
   *eventPP = s;
   *startPtr = NULL;
@@ -3720,8 +3937,16 @@ doIgnoreSection(XML_Parser parser,
     }
     return XML_ERROR_SYNTAX; /* XML_ERROR_UNCLOSED_IGNORE_SECTION */
   default:
+    /* All of the tokens that XmlIgnoreSectionTok() returns have
+     * explicit cases to handle them, so this default case is never
+     * executed.  We keep it as a safety net anyway, and remove it
+     * from our test coverage statistics.
+     *
+     * LCOV_EXCL_START
+     */
     *eventPP = next;
     return XML_ERROR_UNEXPECTED_STATE;
+    /* LCOV_EXCL_STOP */
   }
   /* not reached */
 }
@@ -3734,6 +3959,7 @@ initializeEncoding(XML_Parser parser)
   const char *s;
 #ifdef XML_UNICODE
   char encodingBuf[128];
+  /* See comments abount `protoclEncodingName` in parserInit() */
   if (!protocolEncodingName)
     s = NULL;
   else {
@@ -3817,7 +4043,14 @@ processXmlDecl(XML_Parser parser, int isGeneralTextEntity,
     reportDefault(parser, encoding, s, next);
   if (protocolEncodingName == NULL) {
     if (newEncoding) {
-      if (newEncoding->minBytesPerChar != encoding->minBytesPerChar) {
+      /* Check that the specified encoding does not conflict with what
+       * the parser has already deduced.  Do we have the same number
+       * of bytes in the smallest representation of a character?  If
+       * this is UTF-16, is it the same endianness?
+       */
+      if (newEncoding->minBytesPerChar != encoding->minBytesPerChar
+          || (newEncoding->minBytesPerChar == 2 &&
+              newEncoding != encoding)) {
         eventPtr = encodingName;
         return XML_ERROR_INCORRECT_ENCODING;
       }
@@ -3962,15 +4195,14 @@ entityValueInitProcessor(XML_Parser parser,
       result = processXmlDecl(parser, 0, start, next);
       if (result != XML_ERROR_NONE)
         return result;
-      switch (ps_parsing) {
-      case XML_SUSPENDED:
-        *nextPtr = next;
-        return XML_ERROR_NONE;
-      case XML_FINISHED:
+      /* At this point, ps_parsing cannot be XML_SUSPENDED.  For that
+       * to happen, a parameter entity parsing handler must have
+       * attempted to suspend the parser, which fails and raises an
+       * error.  The parser can be aborted, but can't be suspended.
+       */
+      if (ps_parsing == XML_FINISHED)
         return XML_ERROR_ABORTED;
-      default:
-        *nextPtr = next;
-      }
+      *nextPtr = next;
       /* stop scanning for text declaration - we found one */
       processor = entityValueProcessor;
       return entityValueProcessor(parser, next, end, nextPtr);
@@ -4293,8 +4525,14 @@ doProlog(XML_Parser parser,
                                             &dtd->paramEntities,
                                             externalSubsetName,
                                             sizeof(ENTITY));
-          if (!entity)
-            return XML_ERROR_NO_MEMORY;
+          if (!entity) {
+            /* The external subset name "#" will have already been
+             * inserted into the hash table at the start of the
+             * external entity parsing, so no allocation will happen
+             * and lookup() cannot fail.
+             */
+            return XML_ERROR_NO_MEMORY; /* LCOV_EXCL_LINE */
+          }
           if (useForeignDTD)
             entity->base = curBase;
           dtd->paramEntityRead = XML_FALSE;
@@ -4773,8 +5011,10 @@ doProlog(XML_Parser parser,
       if (prologState.level >= groupSize) {
         if (groupSize) {
           char *temp = (char *)REALLOC(groupConnector, groupSize *= 2);
-          if (temp == NULL)
+          if (temp == NULL) {
+            groupSize /= 2;
             return XML_ERROR_NO_MEMORY;
+          }
           groupConnector = temp;
           if (dtd->scaffIndex) {
             int *temp = (int *)REALLOC(dtd->scaffIndex,
@@ -4786,8 +5026,10 @@ doProlog(XML_Parser parser,
         }
         else {
           groupConnector = (char *)MALLOC(groupSize = 32);
-          if (!groupConnector)
+          if (!groupConnector) {
+            groupSize = 0;
             return XML_ERROR_NO_MEMORY;
+          }
         }
       }
       groupConnector[prologState.level] = 0;
@@ -4850,8 +5092,29 @@ doProlog(XML_Parser parser,
              : !dtd->hasParamEntityRefs)) {
           if (!entity)
             return XML_ERROR_UNDEFINED_ENTITY;
-          else if (!entity->is_internal)
-            return XML_ERROR_ENTITY_DECLARED_IN_PE;
+          else if (!entity->is_internal) {
+            /* It's hard to exhaustively search the code to be sure,
+             * but there doesn't seem to be a way of executing the
+             * following line.  There are two cases:
+             *
+             * If 'standalone' is false, the DTD must have no
+             * parameter entities or we wouldn't have passed the outer
+             * 'if' statement.  That measn the only entity in the hash
+             * table is the external subset name "#" which cannot be
+             * given as a parameter entity name in XML syntax, so the
+             * lookup must have returned NULL and we don't even reach
+             * the test for an internal entity.
+             *
+             * If 'standalone' is true, it does not seem to be
+             * possible to create entities taking this code path that
+             * are not internal entities, so fail the test above.
+             *
+             * Because this analysis is very uncertain, the code is
+             * being left in place and merely removed from the
+             * coverage test statistics.
+             */
+            return XML_ERROR_ENTITY_DECLARED_IN_PE; /* LCOV_EXCL_LINE */
+          }
         }
         else if (!entity) {
           dtd->keepProcessing = dtd->standalone;
@@ -5323,11 +5586,15 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
             && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20))
           break;
         n = XmlEncode(n, (ICHAR *)buf);
-        if (!n) {
-          if (enc == encoding)
-            eventPtr = ptr;
-          return XML_ERROR_BAD_CHAR_REF;
-        }
+        /* The XmlEncode() functions can never return 0 here.  That
+         * error return happens if the code point passed in is either
+         * negative or greater than or equal to 0x110000.  The
+         * XmlCharRefNumber() functions will all return a number
+         * strictly less than 0x110000 or a negative value if an error
+         * occurred.  The negative value is intercepted above, so
+         * XmlEncode() is never passed a value it might return an
+         * error for.
+         */
         for (i = 0; i < n; i++) {
           if (!poolAppendChar(pool, buf[i]))
             return XML_ERROR_NO_MEMORY;
@@ -5401,8 +5668,26 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
           break;
         }
         if (entity->open) {
-          if (enc == encoding)
-            eventPtr = ptr;
+          if (enc == encoding) {
+            /* It does not appear that this line can be executed.
+             *
+             * The "if (entity->open)" check catches recursive entity
+             * definitions.  In order to be called with an open
+             * entity, it must have gone through this code before and
+             * been through the recursive call to
+             * appendAttributeValue() some lines below.  That call
+             * sets the local encoding ("enc") to the parser's
+             * internal encoding (internal_utf8 or internal_utf16),
+             * which can never be the same as the principle encoding.
+             * It doesn't appear there is another code path that gets
+             * here with entity->open being TRUE.
+             *
+             * Since it is not certain that this logic is watertight,
+             * we keep the line and merely exclude it from coverage
+             * tests.
+             */
+            eventPtr = ptr; /* LCOV_EXCL_LINE */
+          }
           return XML_ERROR_RECURSIVE_ENTITY_REF;
         }
         if (entity->notation) {
@@ -5429,9 +5714,21 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
       }
       break;
     default:
+      /* The only token returned by XmlAttributeValueTok() that does
+       * not have an explicit case here is XML_TOK_PARTIAL_CHAR.
+       * Getting that would require an entity name to contain an
+       * incomplete XML character (e.g. \xE2\x82); however previous
+       * tokenisers will have already recognised and rejected such
+       * names before XmlAttributeValueTok() gets a look-in.  This
+       * default case should be retained as a safety net, but the code
+       * excluded from coverage tests.
+       *
+       * LCOV_EXCL_START
+       */
       if (enc == encoding)
         eventPtr = ptr;
       return XML_ERROR_UNEXPECTED_STATE;
+      /* LCOV_EXCL_STOP */
     }
     ptr = next;
   }
@@ -5564,12 +5861,15 @@ storeEntityValue(XML_Parser parser,
           goto endEntityValue;
         }
         n = XmlEncode(n, (ICHAR *)buf);
-        if (!n) {
-          if (enc == encoding)
-            eventPtr = entityTextPtr;
-          result = XML_ERROR_BAD_CHAR_REF;
-          goto endEntityValue;
-        }
+        /* The XmlEncode() functions can never return 0 here.  That
+         * error return happens if the code point passed in is either
+         * negative or greater than or equal to 0x110000.  The
+         * XmlCharRefNumber() functions will all return a number
+         * strictly less than 0x110000 or a negative value if an error
+         * occurred.  The negative value is intercepted above, so
+         * XmlEncode() is never passed a value it might return an
+         * error for.
+         */
         for (i = 0; i < n; i++) {
           if (pool->end == pool->ptr && !poolGrow(pool)) {
             result = XML_ERROR_NO_MEMORY;
@@ -5590,10 +5890,18 @@ storeEntityValue(XML_Parser parser,
       result = XML_ERROR_INVALID_TOKEN;
       goto endEntityValue;
     default:
+      /* This default case should be unnecessary -- all the tokens
+       * that XmlEntityValueTok() can return have their own explicit
+       * cases -- but should be retained for safety.  We do however
+       * exclude it from the coverage statistics.
+       *
+       * LCOV_EXCL_START
+       */
       if (enc == encoding)
         eventPtr = entityTextPtr;
       result = XML_ERROR_UNEXPECTED_STATE;
       goto endEntityValue;
+      /* LCOV_EXCL_STOP */
     }
     entityTextPtr = next;
   }
@@ -5691,8 +5999,25 @@ reportDefault(XML_Parser parser, const ENCODING *enc,
       eventEndPP = &eventEndPtr;
     }
     else {
+      /* To get here, two things must be true; the parser must be
+       * using a character encoding that is not the same as the
+       * encoding passed in, and the encoding passed in must need
+       * conversion to the internal format (UTF-8 unless XML_UNICODE
+       * is defined).  The only occasions on which the encoding passed
+       * in is not the same as the parser's encoding are when it is
+       * the internal encoding (e.g. a previously defined parameter
+       * entity, already converted to internal format).  This by
+       * definition doesn't need conversion, so the whole branch never
+       * gets executed.
+       *
+       * For safety's sake we don't delete these lines and merely
+       * exclude them from coverage statistics.
+       *
+       * LCOV_EXCL_START
+       */
       eventPP = &(openInternalEntities->internalEventPtr);
       eventEndPP = &(openInternalEntities->internalEventEndPtr);
+      /* LCOV_EXCL_STOP */
     }
     do {
       ICHAR *dataPtr = (ICHAR *)dataBuf;
@@ -5861,9 +6186,30 @@ getContext(XML_Parser parser)
     len = dtd->defaultPrefix.binding->uriLen;
     if (namespaceSeparator)
       len--;
-    for (i = 0; i < len; i++)
-      if (!poolAppendChar(&tempPool, dtd->defaultPrefix.binding->uri[i]))
-        return NULL;
+    for (i = 0; i < len; i++) {
+      if (!poolAppendChar(&tempPool, dtd->defaultPrefix.binding->uri[i])) {
+        /* Because of memory caching, I don't believe this line can be
+         * executed.
+         *
+         * This is part of a loop copying the default prefix binding
+         * URI into the parser's temporary string pool.  Previously,
+         * that URI was copied into the same string pool, with a
+         * terminating NUL character, as part of setContext().  When
+         * the pool was cleared, that leaves a block definitely big
+         * enough to hold the URI on the free block list of the pool.
+         * The URI copy in getContext() therefore cannot run out of
+         * memory.
+         *
+         * If the pool is used between the setContext() and
+         * getContext() calls, the worst it can do is leave a bigger
+         * block on the front of the free list.  Given that this is
+         * all somewhat inobvious and program logic can be changed, we
+         * don't delete the line but we do exclude it from the test
+         * coverage statistics.
+         */
+        return NULL; /* LCOV_EXCL_LINE */
+      }
+    }
     needSep = XML_TRUE;
   }
 
@@ -5875,8 +6221,15 @@ getContext(XML_Parser parser)
     PREFIX *prefix = (PREFIX *)hashTableIterNext(&iter);
     if (!prefix)
       break;
-    if (!prefix->binding)
-      continue;
+    if (!prefix->binding) {
+      /* This test appears to be (justifiable) paranoia.  There does
+       * not seem to be a way of injecting a prefix without a binding
+       * that doesn't get errored long before this function is called.
+       * The test should remain for safety's sake, so we instead
+       * exclude the following line from the coverage statistics.
+       */
+      continue; /* LCOV_EXCL_LINE */
+    }
     if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP))
       return NULL;
     for (s = prefix->name; *s; s++)
@@ -6547,8 +6900,20 @@ poolCopyString(STRING_POOL *pool, const XML_Char *s)
 static const XML_Char *
 poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n)
 {
-  if (!pool->ptr && !poolGrow(pool))
-    return NULL;
+  if (!pool->ptr && !poolGrow(pool)) {
+    /* The following line is unreachable given the current usage of
+     * poolCopyStringN().  Currently it is called from exactly one
+     * place to copy the text of a simple general entity.  By that
+     * point, the name of the entity is already stored in the pool, so
+     * pool->ptr cannot be NULL.
+     *
+     * If poolCopyStringN() is used elsewhere as it well might be,
+     * this line may well become executable again.  Regardless, this
+     * sort of check shouldn't be removed lightly, so we just exclude
+     * it from the coverage statistics.
+     */
+    return NULL; /* LCOV_EXCL_LINE */
+  }
   for (; n > 0; --n, s++) {
     if (!poolAppendChar(pool, *s))
       return NULL;
@@ -6641,8 +7006,19 @@ poolGrow(STRING_POOL *pool)
     int blockSize = (int)((unsigned)(pool->end - pool->start)*2U);
     size_t bytesToAllocate;
 
-    if (blockSize < 0)
-      return XML_FALSE;
+    // NOTE: Needs to be calculated prior to calling `realloc`
+    //       to avoid dangling pointers:
+    const ptrdiff_t offsetInsideBlock = pool->ptr - pool->start;
+
+    if (blockSize < 0) {
+      /* This condition traps a situation where either more than
+       * INT_MAX/2 bytes have already been allocated.  This isn't
+       * readily testable, since it is unlikely that an average
+       * machine will have that much memory, so we exclude it from the
+       * coverage statistics.
+       */
+      return XML_FALSE; /* LCOV_EXCL_LINE */
+    }
 
     bytesToAllocate = poolBytesToAllocateFor(blockSize);
     if (bytesToAllocate == 0)
@@ -6654,7 +7030,7 @@ poolGrow(STRING_POOL *pool)
       return XML_FALSE;
     pool->blocks = temp;
     pool->blocks->size = blockSize;
-    pool->ptr = pool->blocks->s + (pool->ptr - pool->start);
+    pool->ptr = pool->blocks->s + offsetInsideBlock;
     pool->start = pool->blocks->s;
     pool->end = pool->start + blockSize;
   }
@@ -6663,8 +7039,18 @@ poolGrow(STRING_POOL *pool)
     int blockSize = (int)(pool->end - pool->start);
     size_t bytesToAllocate;
 
-    if (blockSize < 0)
-      return XML_FALSE;
+    if (blockSize < 0) {
+      /* This condition traps a situation where either more than
+       * INT_MAX bytes have already been allocated (which is prevented
+       * by various pieces of program logic, not least this one, never
+       * mind the unlikelihood of actually having that much memory) or
+       * the pool control fields have been corrupted (which could
+       * conceivably happen in an extremely buggy user handler
+       * function).  Either way it isn't readily testable, so we
+       * exclude it from the coverage statistics.
+       */
+      return XML_FALSE;  /* LCOV_EXCL_LINE */
+    }
 
     if (blockSize < INIT_BLOCK_SIZE)
       blockSize = INIT_BLOCK_SIZE;
@@ -6827,3 +7213,26 @@ getElementType(XML_Parser parser,
   }
   return ret;
 }
+
+static XML_Char *
+copyString(const XML_Char *s,
+           const XML_Memory_Handling_Suite *memsuite)
+{
+    int charsRequired = 0;
+    XML_Char *result;
+
+    /* First determine how long the string is */
+    while (s[charsRequired] != 0) {
+      charsRequired++;
+    }
+    /* Include the terminator */
+    charsRequired++;
+
+    /* Now allocate space for the copy */
+    result = memsuite->malloc_fcn(charsRequired * sizeof(XML_Char));
+    if (result == NULL)
+        return NULL;
+    /* Copy the original into place */
+    memcpy(result, s, charsRequired * sizeof(XML_Char));
+    return result;
+}
index a7c5630..708507d 100644 (file)
@@ -1,5 +1,33 @@
-/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
-   See the file COPYING for copying permission.
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000-2017 Expat development team
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
 #include <stddef.h>
@@ -170,7 +198,14 @@ prolog1(PROLOG_STATE *state,
   case XML_TOK_COMMENT:
     return XML_ROLE_COMMENT;
   case XML_TOK_BOM:
-    return XML_ROLE_NONE;
+    /* This case can never arise.  To reach this role function, the
+     * parse must have passed through prolog0 and therefore have had
+     * some form of input, even if only a space.  At that point, a
+     * byte order mark is no longer a valid character (though
+     * technically it should be interpreted as a non-breaking space),
+     * so will be rejected by the tokenizing stages.
+     */
+    return XML_ROLE_NONE; /* LCOV_EXCL_LINE */
   case XML_TOK_DECL_OPEN:
     if (!XmlNameMatchesAscii(enc,
                              ptr + 2 * MIN_BYTES_PER_CHAR(enc),
@@ -1285,6 +1320,26 @@ declClose(PROLOG_STATE *state,
   return common(state, tok);
 }
 
+/* This function will only be invoked if the internal logic of the
+ * parser has broken down.  It is used in two cases:
+ *
+ * 1: When the XML prolog has been finished.  At this point the
+ * processor (the parser level above these role handlers) should
+ * switch from prologProcessor to contentProcessor and reinitialise
+ * the handler function.
+ *
+ * 2: When an error has been detected (via common() below).  At this
+ * point again the processor should be switched to errorProcessor,
+ * which will never call a handler.
+ *
+ * The result of this is that error() can only be called if the
+ * processor switch failed to happen, which is an internal error and
+ * therefore we shouldn't be able to provoke it simply by using the
+ * library.  It is a necessary backstop, however, so we merely exclude
+ * it from the coverage statistics.
+ *
+ * LCOV_EXCL_START
+ */
 static int PTRCALL
 error(PROLOG_STATE *UNUSED_P(state),
       int UNUSED_P(tok),
@@ -1294,6 +1349,7 @@ error(PROLOG_STATE *UNUSED_P(state),
 {
   return XML_ROLE_NONE;
 }
+/* LCOV_EXCL_STOP */
 
 static int FASTCALL
 common(PROLOG_STATE *state, int tok)
index 4dd9f06..e5f048e 100644 (file)
@@ -1,5 +1,33 @@
-/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
-   See the file COPYING for copying permission.
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000-2017 Expat development team
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
 #ifndef XmlRole_INCLUDED
index cdf0720..007aed0 100644 (file)
@@ -1,8 +1,38 @@
-/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
-   See the file COPYING for copying permission.
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000-2017 Expat development team
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
 #include <stddef.h>
+#include <stdbool.h>
+#include <string.h>  // memcpy
 
 #ifdef _WIN32
 #include "winconfig.h"
@@ -363,22 +393,33 @@ utf8_toUtf8(const ENCODING *UNUSED_P(enc),
             const char **fromP, const char *fromLim,
             char **toP, const char *toLim)
 {
-  char *to;
-  const char *from;
-  const char *fromLimInitial = fromLim;
+  bool input_incomplete = false;
+  bool output_exhausted = false;
+
+  /* Avoid copying partial characters (due to limited space). */
+  const ptrdiff_t bytesAvailable = fromLim - *fromP;
+  const ptrdiff_t bytesStorable = toLim - *toP;
+  if (bytesAvailable > bytesStorable) {
+    fromLim = *fromP + bytesStorable;
+    output_exhausted = true;
+  }
 
-  /* Avoid copying partial characters. */
+  /* Avoid copying partial characters (from incomplete input). */
+  const char * const fromLimBefore = fromLim;
   align_limit_to_full_utf8_characters(*fromP, &fromLim);
+  if (fromLim < fromLimBefore) {
+    input_incomplete = true;
+  }
 
-  for (to = *toP, from = *fromP; (from < fromLim) && (to < toLim); from++, to++)
-    *to = *from;
-  *fromP = from;
-  *toP = to;
+  const ptrdiff_t bytesToCopy = fromLim - *fromP;
+  memcpy((void *)*toP, (const void *)*fromP, (size_t)bytesToCopy);
+  *fromP += bytesToCopy;
+  *toP += bytesToCopy;
 
-  if (fromLim < fromLimInitial)
-    return XML_CONVERT_INPUT_INCOMPLETE;
-  else if ((to == toLim) && (from < fromLim))
+  if (output_exhausted)  // needs to go first
     return XML_CONVERT_OUTPUT_EXHAUSTED;
+  else if (input_incomplete)
+    return XML_CONVERT_INPUT_INCOMPLETE;
   else
     return XML_CONVERT_COMPLETED;
 }
@@ -1019,7 +1060,11 @@ streqci(const char *s1, const char *s2)
     if (ASCII_a <= c1 && c1 <= ASCII_z)
       c1 += ASCII_A - ASCII_a;
     if (ASCII_a <= c2 && c2 <= ASCII_z)
-      c2 += ASCII_A - ASCII_a;
+      /* The following line will never get executed.  streqci() is
+       * only called from two places, both of which guarantee to put
+       * upper-case strings into s2.
+       */
+      c2 += ASCII_A - ASCII_a; /* LCOV_EXCL_LINE */
     if (c1 != c2)
       return 0;
     if (!c1)
@@ -1291,7 +1336,7 @@ XmlUtf8Encode(int c, char *buf)
   };
 
   if (c < 0)
-    return 0;
+    return 0; /* LCOV_EXCL_LINE: this case is always eliminated beforehand */
   if (c < min2) {
     buf[0] = (char)(c | UTF8_cval1);
     return 1;
@@ -1314,7 +1359,7 @@ XmlUtf8Encode(int c, char *buf)
     buf[3] = (char)((c & 0x3f) | 0x80);
     return 4;
   }
-  return 0;
+  return 0; /* LCOV_EXCL_LINE: this case too is eliminated before calling */
 }
 
 int FASTCALL
@@ -1465,6 +1510,9 @@ XmlInitUnknownEncoding(void *mem,
     else if (c < 0) {
       if (c < -4)
         return 0;
+      /* Multi-byte sequences need a converter function */
+      if (!convert)
+        return 0;
       e->normal.type[i] = (unsigned char)(BT_LEAD2 - (c + 2));
       e->utf8[i][0] = 0;
       e->utf16[i] = 0;
index 752007e..6d31879 100644 (file)
@@ -1,5 +1,33 @@
-/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
-   See the file COPYING for copying permission.
+/*
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000-2017 Expat development team
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
 #ifndef XmlTok_INCLUDED
index 5f779c0..93328b8 100644 (file)
@@ -1,8 +1,35 @@
-/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
-   See the file COPYING for copying permission.
+/* This file is included!
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000-2017 Expat development team
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
-/* This file is included! */
 #ifdef XML_TOK_IMPL_C
 
 #ifndef IS_INVALID_CHAR
@@ -1198,8 +1225,14 @@ PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr,
   const char *start;
   if (ptr >= end)
     return XML_TOK_NONE;
-  else if (! HAS_CHAR(enc, ptr, end))
-    return XML_TOK_PARTIAL;
+  else if (! HAS_CHAR(enc, ptr, end)) {
+    /* This line cannot be executed.  The incoming data has already
+     * been tokenized once, so incomplete characters like this have
+     * already been eliminated from the input.  Retaining the paranoia
+     * check is still valuable, however.
+     */
+    return XML_TOK_PARTIAL; /* LCOV_EXCL_LINE */
+  }
   start = ptr;
   while (HAS_CHAR(enc, ptr, end)) {
     switch (BYTE_TYPE(enc, ptr)) {
@@ -1258,8 +1291,14 @@ PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr,
   const char *start;
   if (ptr >= end)
     return XML_TOK_NONE;
-  else if (! HAS_CHAR(enc, ptr, end))
-    return XML_TOK_PARTIAL;
+  else if (! HAS_CHAR(enc, ptr, end)) {
+    /* This line cannot be executed.  The incoming data has already
+     * been tokenized once, so incomplete characters like this have
+     * already been eliminated from the input.  Retaining the paranoia
+     * check is still valuable, however.
+     */
+    return XML_TOK_PARTIAL; /* LCOV_EXCL_LINE */
+  }
   start = ptr;
   while (HAS_CHAR(enc, ptr, end)) {
     switch (BYTE_TYPE(enc, ptr)) {
@@ -1614,6 +1653,14 @@ PREFIX(predefinedEntityName)(const ENCODING *UNUSED_P(enc), const char *ptr,
   return 0;
 }
 
+/* This function does not appear to be called from anywhere within the
+ * library code.  It is used via the macro XmlSameName(), which is
+ * defined but never used.  Since it appears in the encoding function
+ * table, removing it is not a thing to be undertaken lightly.  For
+ * the moment, we simply exclude it from coverage tests.
+ *
+ * LCOV_EXCL_START
+ */
 static int PTRCALL
 PREFIX(sameName)(const ENCODING *enc, const char *ptr1, const char *ptr2)
 {
@@ -1677,14 +1724,21 @@ PREFIX(sameName)(const ENCODING *enc, const char *ptr1, const char *ptr2)
   }
   /* not reached */
 }
+/* LCOV_EXCL_STOP */
 
 static int PTRCALL
 PREFIX(nameMatchesAscii)(const ENCODING *UNUSED_P(enc), const char *ptr1,
                          const char *end1, const char *ptr2)
 {
   for (; *ptr2; ptr1 += MINBPC(enc), ptr2++) {
-    if (end1 - ptr1 < MINBPC(enc))
-      return 0;
+    if (end1 - ptr1 < MINBPC(enc)) {
+      /* This line cannot be executed.  THe incoming data has already
+       * been tokenized once, so imcomplete characters like this have
+       * already been eliminated from the input.  Retaining the
+       * paranoia check is still valuable, however.
+       */
+      return 0; /* LCOV_EXCL_LINE */
+    }
     if (!CHAR_MATCHES(enc, ptr1, *ptr2))
       return 0;
   }
index da0ea60..a6420f4 100644 (file)
@@ -1,6 +1,33 @@
 /*
-Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
-See the file COPYING for copying permission.
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000-2017 Expat development team
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
 enum {
index c3b88fd..23d31e8 100644 (file)
@@ -1,8 +1,35 @@
-/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
-   See the file COPYING for copying permission.
+/* This file is included!
+                            __  __            _
+                         ___\ \/ /_ __   __ _| |_
+                        / _ \\  /| '_ \ / _` | __|
+                       |  __//  \| |_) | (_| | |_
+                        \___/_/\_\ .__/ \__,_|\__|
+                                 |_| XML parser
+
+   Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+   Copyright (c) 2000-2017 Expat development team
+   Licensed under the MIT license:
+
+   Permission is  hereby granted,  free of charge,  to any  person obtaining
+   a  copy  of  this  software   and  associated  documentation  files  (the
+   "Software"),  to  deal in  the  Software  without restriction,  including
+   without  limitation the  rights  to use,  copy,  modify, merge,  publish,
+   distribute, sublicense, and/or sell copies of the Software, and to permit
+   persons  to whom  the Software  is  furnished to  do so,  subject to  the
+   following conditions:
+
+   The above copyright  notice and this permission notice  shall be included
+   in all copies or substantial portions of the Software.
+
+   THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
+   EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+   NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+   DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
+   OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+   USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
-/* This file is included! */
 #ifdef XML_TOK_NS_C
 
 const ENCODING *
index 9437ae7..f577fd3 100644 (file)
@@ -151,6 +151,7 @@ grp_getgrnam_impl(PyObject *module, PyObject *name)
 
     if ((bytes = PyUnicode_EncodeFSDefault(name)) == NULL)
         return NULL;
+    /* check for embedded null bytes */
     if (PyBytes_AsStringAndSize(bytes, &name_chars, NULL) == -1)
         goto out;
 
index b6a855c..a9028bb 100644 (file)
@@ -169,6 +169,7 @@ nis_match (PyObject *self, PyObject *args, PyObject *kwdict)
         return NULL;
     if ((bkey = PyUnicode_EncodeFSDefault(ukey)) == NULL)
         return NULL;
+    /* check for embedded null bytes */
     if (PyBytes_AsStringAndSize(bkey, &key, &keylen) == -1) {
         Py_DECREF(bkey);
         return NULL;
index 0aa8657..17581e0 100644 (file)
@@ -1151,7 +1151,7 @@ ConnectPipe(OverlappedObject *self, PyObject *args)
     if (!PyArg_ParseTuple(args, "U",  &AddressObj))
         return NULL;
 
-    Address = PyUnicode_AsWideCharString(AddressObj, NULL);
+    Address = _PyUnicode_AsWideCharString(AddressObj);
     if (Address == NULL)
         return NULL;
 
index 0a9123b..ee27fa4 100644 (file)
@@ -55,6 +55,11 @@ corresponding Unix manual entries for more information on calls.");
 #include <sys/uio.h>
 #endif
 
+#ifdef HAVE_SYS_SYSMACROS_H
+/* GNU C Library: major(), minor(), makedev() */
+#include <sys/sysmacros.h>
+#endif
+
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif /* HAVE_SYS_TYPES_H */
@@ -949,6 +954,8 @@ path_converter(PyObject *o, void *p)
         Py_INCREF(bytes);
     }
     else if (is_buffer) {
+        /* XXX Replace PyObject_CheckBuffer with PyBytes_Check in other code
+           after removing suport of non-bytes buffer objects. */
         if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
             "%s%s%s should be %s, not %.200s",
             path->function_name ? path->function_name : "",
@@ -1932,14 +1939,8 @@ _pystat_fromstructstat(STRUCT_STAT *st)
         return NULL;
 
     PyStructSequence_SET_ITEM(v, 0, PyLong_FromLong((long)st->st_mode));
-#if defined(HAVE_LARGEFILE_SUPPORT) || defined(MS_WINDOWS)
     Py_BUILD_ASSERT(sizeof(unsigned long long) >= sizeof(st->st_ino));
-    PyStructSequence_SET_ITEM(v, 1,
-                              PyLong_FromUnsignedLongLong(st->st_ino));
-#else
-    Py_BUILD_ASSERT(sizeof(unsigned long) >= sizeof(st->st_ino));
-    PyStructSequence_SET_ITEM(v, 1, PyLong_FromUnsignedLong(st->st_ino));
-#endif
+    PyStructSequence_SET_ITEM(v, 1, PyLong_FromUnsignedLongLong(st->st_ino));
 #ifdef MS_WINDOWS
     PyStructSequence_SET_ITEM(v, 2, PyLong_FromUnsignedLong(st->st_dev));
 #else
@@ -1953,12 +1954,8 @@ _pystat_fromstructstat(STRUCT_STAT *st)
     PyStructSequence_SET_ITEM(v, 4, _PyLong_FromUid(st->st_uid));
     PyStructSequence_SET_ITEM(v, 5, _PyLong_FromGid(st->st_gid));
 #endif
-#ifdef HAVE_LARGEFILE_SUPPORT
-    PyStructSequence_SET_ITEM(v, 6,
-                              PyLong_FromLongLong((long long)st->st_size));
-#else
-    PyStructSequence_SET_ITEM(v, 6, PyLong_FromLong(st->st_size));
-#endif
+    Py_BUILD_ASSERT(sizeof(long long) >= sizeof(st->st_size));
+    PyStructSequence_SET_ITEM(v, 6, PyLong_FromLongLong(st->st_size));
 
 #if defined(HAVE_STAT_TV_NSEC)
     ansec = st->st_atim.tv_nsec;
@@ -3511,8 +3508,8 @@ _posix_listdir(path_t *path, PyObject *list)
         const char *name;
         if (path->narrow) {
             name = path->narrow;
-            /* only return bytes if they specified a bytes object */
-            return_str = !(PyBytes_Check(path->object));
+            /* only return bytes if they specified a bytes-like object */
+            return_str = !PyObject_CheckBuffer(path->object);
         }
         else {
             name = ".";
@@ -3680,7 +3677,7 @@ os__getfinalpathname_impl(PyObject *module, PyObject *path)
     PyObject *result;
     const wchar_t *path_wchar;
 
-    path_wchar = PyUnicode_AsUnicode(path);
+    path_wchar = _PyUnicode_AsUnicode(path);
     if (path_wchar == NULL)
         return NULL;
 
@@ -4817,6 +4814,16 @@ parse_envlist(PyObject* env, Py_ssize_t *envc_ptr)
             Py_DECREF(key2);
             goto error;
         }
+        /* Search from index 1 because on Windows starting '=' is allowed for
+           defining hidden environment variables. */
+        if (PyUnicode_GET_LENGTH(key2) == 0 ||
+            PyUnicode_FindChar(key2, '=', 1, PyUnicode_GET_LENGTH(key2), 1) != -1)
+        {
+            PyErr_SetString(PyExc_ValueError, "illegal environment variable name");
+            Py_DECREF(key2);
+            Py_DECREF(val2);
+            goto error;
+        }
         keyval = PyUnicode_FromFormat("%U=%U", key2, val2);
 #else
         if (!PyUnicode_FSConverter(key, &key2))
@@ -4825,6 +4832,14 @@ parse_envlist(PyObject* env, Py_ssize_t *envc_ptr)
             Py_DECREF(key2);
             goto error;
         }
+        if (PyBytes_GET_SIZE(key2) == 0 ||
+            strchr(PyBytes_AS_STRING(key2) + 1, '=') != NULL)
+        {
+            PyErr_SetString(PyExc_ValueError, "illegal environment variable name");
+            Py_DECREF(key2);
+            Py_DECREF(val2);
+            goto error;
+        }
         keyval = PyBytes_FromFormat("%s=%s", PyBytes_AS_STRING(key2),
                                              PyBytes_AS_STRING(val2));
 #endif
@@ -5088,7 +5103,7 @@ os_spawnv_impl(PyObject *module, int mode, path_t *path, PyObject *argv)
             return NULL;
         }
         if (i == 0 && !argvlist[0][0]) {
-            free_string_array(argvlist, i);
+            free_string_array(argvlist, i + 1);
             PyErr_SetString(
                 PyExc_ValueError,
                 "spawnv() arg 2 first element cannot be empty");
@@ -5189,7 +5204,7 @@ os_spawnve_impl(PyObject *module, int mode, path_t *path, PyObject *argv,
             goto fail_1;
         }
         if (i == 0 && !argvlist[0][0]) {
-            lastarg = i;
+            lastarg = i + 1;
             PyErr_SetString(
                 PyExc_ValueError,
                 "spawnv() arg 2 first element cannot be empty");
@@ -7070,7 +7085,7 @@ win_readlink(PyObject *self, PyObject *args, PyObject *kwargs)
                           ))
         return NULL;
 
-    path = PyUnicode_AsUnicode(po);
+    path = _PyUnicode_AsUnicode(po);
     if (path == NULL)
         return NULL;
 
@@ -8863,22 +8878,35 @@ os_putenv_impl(PyObject *module, PyObject *name, PyObject *value)
 /*[clinic end generated code: output=d29a567d6b2327d2 input=ba586581c2e6105f]*/
 {
     const wchar_t *env;
+    Py_ssize_t size;
 
+    /* Search from index 1 because on Windows starting '=' is allowed for
+       defining hidden environment variables. */
+    if (PyUnicode_GET_LENGTH(name) == 0 ||
+        PyUnicode_FindChar(name, '=', 1, PyUnicode_GET_LENGTH(name), 1) != -1)
+    {
+        PyErr_SetString(PyExc_ValueError, "illegal environment variable name");
+        return NULL;
+    }
     PyObject *unicode = PyUnicode_FromFormat("%U=%U", name, value);
     if (unicode == NULL) {
-        PyErr_NoMemory();
         return NULL;
     }
-    if (_MAX_ENV < PyUnicode_GET_LENGTH(unicode)) {
+
+    env = PyUnicode_AsUnicodeAndSize(unicode, &size);
+    if (env == NULL)
+        goto error;
+    if (size > _MAX_ENV) {
         PyErr_Format(PyExc_ValueError,
                      "the environment variable is longer than %u characters",
                      _MAX_ENV);
         goto error;
     }
-
-    env = PyUnicode_AsUnicode(unicode);
-    if (env == NULL)
+    if (wcslen(env) != (size_t)size) {
+        PyErr_SetString(PyExc_ValueError, "embedded null character");
         goto error;
+    }
+
     if (_wputenv(env)) {
         posix_error();
         goto error;
@@ -8908,12 +8936,15 @@ os_putenv_impl(PyObject *module, PyObject *name, PyObject *value)
 {
     PyObject *bytes = NULL;
     char *env;
-    const char *name_string = PyBytes_AsString(name);
-    const char *value_string = PyBytes_AsString(value);
+    const char *name_string = PyBytes_AS_STRING(name);
+    const char *value_string = PyBytes_AS_STRING(value);
 
+    if (strchr(name_string, '=') != NULL) {
+        PyErr_SetString(PyExc_ValueError, "illegal environment variable name");
+        return NULL;
+    }
     bytes = PyBytes_FromFormat("%s=%s", name_string, value_string);
     if (bytes == NULL) {
-        PyErr_NoMemory();
         return NULL;
     }
 
@@ -11002,9 +11033,22 @@ os_cpu_count_impl(PyObject *module)
 {
     int ncpu = 0;
 #ifdef MS_WINDOWS
-    SYSTEM_INFO sysinfo;
-    GetSystemInfo(&sysinfo);
-    ncpu = sysinfo.dwNumberOfProcessors;
+    /* Vista is supported and the GetMaximumProcessorCount API is Win7+
+       Need to fallback to Vista behavior if this call isn't present */
+    HINSTANCE hKernel32;
+    hKernel32 = GetModuleHandleW(L"KERNEL32");
+
+    static DWORD(CALLBACK *_GetMaximumProcessorCount)(WORD) = NULL;
+    *(FARPROC*)&_GetMaximumProcessorCount = GetProcAddress(hKernel32,
+        "GetMaximumProcessorCount");
+    if (_GetMaximumProcessorCount != NULL) {
+        ncpu = _GetMaximumProcessorCount(ALL_PROCESSOR_GROUPS);
+    }
+    else {
+        SYSTEM_INFO sysinfo;
+        GetSystemInfo(&sysinfo);
+        ncpu = sysinfo.dwNumberOfProcessors;
+    }
 #elif defined(__hpux)
     ncpu = mpctl(MPC_GETNUMSPUS, NULL, NULL);
 #elif defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
@@ -11448,11 +11492,8 @@ DirEntry_inode(DirEntry *self)
     Py_BUILD_ASSERT(sizeof(unsigned long long) >= sizeof(self->win32_file_index));
     return PyLong_FromUnsignedLongLong(self->win32_file_index);
 #else /* POSIX */
-#ifdef HAVE_LARGEFILE_SUPPORT
-    return PyLong_FromLongLong((long long)self->d_ino);
-#else
-    return PyLong_FromLong((long)self->d_ino);
-#endif
+    Py_BUILD_ASSERT(sizeof(unsigned long long) >= sizeof(self->d_ino));
+    return PyLong_FromUnsignedLongLong(self->d_ino);
 #endif
 }
 
@@ -11674,7 +11715,7 @@ DirEntry_from_posix_info(path_t *path, const char *name, Py_ssize_t name_len,
     if (!joined_path)
         goto error;
 
-    if (!path->narrow || !PyBytes_Check(path->object)) {
+    if (!path->narrow || !PyObject_CheckBuffer(path->object)) {
         entry->name = PyUnicode_DecodeFSDefaultAndSize(name, name_len);
         entry->path = PyUnicode_DecodeFSDefault(joined_path);
     }
index 784e9d0..bbef2de 100644 (file)
@@ -158,6 +158,7 @@ pwd_getpwnam_impl(PyObject *module, PyObject *arg)
 
     if ((bytes = PyUnicode_EncodeFSDefault(arg)) == NULL)
         return NULL;
+    /* check for embedded null bytes */
     if (PyBytes_AsStringAndSize(bytes, &name, NULL) == -1)
         goto out;
     if ((p = getpwnam(name)) == NULL) {
index 3b94d4a..575479c 100644 (file)
@@ -1347,15 +1347,17 @@ call_readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
     if (should_auto_add_history && n > 0) {
         const char *line;
         int length = _py_get_history_length();
-        if (length > 0)
+        if (length > 0) {
+            HIST_ENTRY *hist_ent;
 #ifdef __APPLE__
             if (using_libedit_emulation) {
                 /* handle older 0-based or newer 1-based indexing */
-                line = (const char *)history_get(length + libedit_history_start - 1)->line;
+                hist_ent = history_get(length + libedit_history_start - 1);
             } else
 #endif /* __APPLE__ */
-            line = (const char *)history_get(length)->line;
-        else
+                hist_ent = history_get(length);
+            line = hist_ent ? hist_ent->line : "";
+        } else
             line = "";
         if (strcmp(p, line))
             add_history(p);
index 47da493..07a20c8 100644 (file)
@@ -68,8 +68,8 @@ typedef struct {
 static void
 reap_obj(pylist fd2obj[FD_SETSIZE + 1])
 {
-    int i;
-    for (i = 0; i < FD_SETSIZE + 1 && fd2obj[i].sentinel >= 0; i++) {
+    unsigned int i;
+    for (i = 0; i < (unsigned int)FD_SETSIZE + 1 && fd2obj[i].sentinel >= 0; i++) {
         Py_CLEAR(fd2obj[i].obj);
     }
     fd2obj[0].sentinel = -1;
@@ -83,7 +83,7 @@ static int
 seq2set(PyObject *seq, fd_set *set, pylist fd2obj[FD_SETSIZE + 1])
 {
     int max = -1;
-    int index = 0;
+    unsigned int index = 0;
     Py_ssize_t i;
     PyObject* fast_seq = NULL;
     PyObject* o = NULL;
@@ -120,7 +120,7 @@ seq2set(PyObject *seq, fd_set *set, pylist fd2obj[FD_SETSIZE + 1])
         FD_SET(v, set);
 
         /* add object and its file descriptor to the list */
-        if (index >= FD_SETSIZE) {
+        if (index >= (unsigned int)FD_SETSIZE) {
             PyErr_SetString(PyExc_ValueError,
                           "too many file descriptors in select()");
             goto finally;
index ed2f4e8..0e6099f 100644 (file)
@@ -93,7 +93,7 @@ static pid_t main_pid;
 #endif
 
 static volatile struct {
-    sig_atomic_t tripped;
+    _Py_atomic_int tripped;
     PyObject *func;
 } Handlers[NSIG];
 
@@ -113,7 +113,7 @@ static volatile sig_atomic_t wakeup_fd = -1;
 #endif
 
 /* Speed up sigcheck() when none tripped */
-static volatile sig_atomic_t is_tripped = 0;
+static _Py_atomic_int is_tripped;
 
 static PyObject *DefaultHandler;
 static PyObject *IgnoreHandler;
@@ -139,6 +139,10 @@ timeval_from_double(double d, struct timeval *tv)
 {
     tv->tv_sec = floor(d);
     tv->tv_usec = fmod(d, 1.0) * 1000000.0;
+    /* Don't disable the timer if the computation above rounds down to zero. */
+    if (d > 0.0 && tv->tv_sec == 0 && tv->tv_usec == 0) {
+        tv->tv_usec = 1;
+    }
 }
 
 Py_LOCAL_INLINE(double)
@@ -189,12 +193,6 @@ It raises KeyboardInterrupt.");
 
 
 static int
-checksignals_witharg(void * unused)
-{
-    return PyErr_CheckSignals();
-}
-
-static int
 report_wakeup_write_error(void *data)
 {
     int save_errno = errno;
@@ -242,19 +240,19 @@ trip_signal(int sig_num)
     int fd;
     Py_ssize_t rc;
 
-    Handlers[sig_num].tripped = 1;
+    _Py_atomic_store_relaxed(&Handlers[sig_num].tripped, 1);
 
-    if (!is_tripped) {
-        /* Set is_tripped after setting .tripped, as it gets
-           cleared in PyErr_CheckSignals() before .tripped. */
-        is_tripped = 1;
-        Py_AddPendingCall(checksignals_witharg, NULL);
-    }
+    /* Set is_tripped after setting .tripped, as it gets
+       cleared in PyErr_CheckSignals() before .tripped. */
+    _Py_atomic_store(&is_tripped, 1);
+
+    /* Notify ceval.c */
+    _PyEval_SignalReceived();
 
     /* And then write to the wakeup fd *after* setting all the globals and
-       doing the Py_AddPendingCall. We used to write to the wakeup fd and then
-       set the flag, but this allowed the following sequence of events
-       (especially on windows, where trip_signal runs in a new thread):
+       doing the _PyEval_SignalReceived. We used to write to the wakeup fd
+       and then set the flag, but this allowed the following sequence of events
+       (especially on windows, where trip_signal may run in a new thread):
 
        - main thread blocks on select([wakeup_fd], ...)
        - signal arrives
@@ -289,6 +287,8 @@ trip_signal(int sig_num)
                 wakeup.send_err_set = 1;
                 wakeup.send_errno = errno;
                 wakeup.send_win_error = GetLastError();
+                /* Py_AddPendingCall() isn't signal-safe, but we
+                   still use it for this exceptional case. */
                 Py_AddPendingCall(report_wakeup_send_error, NULL);
             }
         }
@@ -302,6 +302,8 @@ trip_signal(int sig_num)
             rc = _Py_write_noraise(fd, &byte, 1);
 
             if (rc < 0) {
+                /* Py_AddPendingCall() isn't signal-safe, but we
+                   still use it for this exceptional case. */
                 Py_AddPendingCall(report_wakeup_write_error,
                                   (void *)(intptr_t)errno);
             }
@@ -465,7 +467,7 @@ signal_signal_impl(PyObject *module, int signalnum, PyObject *handler)
         return NULL;
     }
     old_handler = Handlers[signalnum].func;
-    Handlers[signalnum].tripped = 0;
+    _Py_atomic_store_relaxed(&Handlers[signalnum].tripped, 0);
     Py_INCREF(handler);
     Handlers[signalnum].func = handler;
     if (old_handler != NULL)
@@ -1264,11 +1266,11 @@ PyInit__signal(void)
         goto finally;
     Py_INCREF(IntHandler);
 
-    Handlers[0].tripped = 0;
+    _Py_atomic_store_relaxed(&Handlers[0].tripped, 0);
     for (i = 1; i < NSIG; i++) {
         void (*t)(int);
         t = PyOS_getsig(i);
-        Handlers[i].tripped = 0;
+        _Py_atomic_store_relaxed(&Handlers[i].tripped, 0);
         if (t == SIG_DFL)
             Handlers[i].func = DefaultHandler;
         else if (t == SIG_IGN)
@@ -1492,7 +1494,7 @@ finisignal(void)
 
     for (i = 1; i < NSIG; i++) {
         func = Handlers[i].func;
-        Handlers[i].tripped = 0;
+        _Py_atomic_store_relaxed(&Handlers[i].tripped, 0);
         Handlers[i].func = NULL;
         if (i != SIGINT && func != NULL && func != Py_None &&
             func != DefaultHandler && func != IgnoreHandler)
@@ -1513,7 +1515,7 @@ PyErr_CheckSignals(void)
     int i;
     PyObject *f;
 
-    if (!is_tripped)
+    if (!_Py_atomic_load(&is_tripped))
         return 0;
 
 #ifdef WITH_THREAD
@@ -1535,24 +1537,26 @@ PyErr_CheckSignals(void)
      *       we receive a signal i after we zero is_tripped and before we
      *       check Handlers[i].tripped.
      */
-    is_tripped = 0;
+    _Py_atomic_store(&is_tripped, 0);
 
     if (!(f = (PyObject *)PyEval_GetFrame()))
         f = Py_None;
 
     for (i = 1; i < NSIG; i++) {
-        if (Handlers[i].tripped) {
+        if (_Py_atomic_load_relaxed(&Handlers[i].tripped)) {
             PyObject *result = NULL;
             PyObject *arglist = Py_BuildValue("(iO)", i, f);
-            Handlers[i].tripped = 0;
+            _Py_atomic_store_relaxed(&Handlers[i].tripped, 0);
 
             if (arglist) {
                 result = PyEval_CallObject(Handlers[i].func,
                                            arglist);
                 Py_DECREF(arglist);
             }
-            if (!result)
+            if (!result) {
+                _Py_atomic_store(&is_tripped, 1);
                 return -1;
+            }
 
             Py_DECREF(result);
         }
@@ -1589,12 +1593,12 @@ PyOS_FiniInterrupts(void)
 int
 PyOS_InterruptOccurred(void)
 {
-    if (Handlers[SIGINT].tripped) {
+    if (_Py_atomic_load_relaxed(&Handlers[SIGINT].tripped)) {
 #ifdef WITH_THREAD
         if (PyThread_get_thread_ident() != main_thread)
             return 0;
 #endif
-        Handlers[SIGINT].tripped = 0;
+        _Py_atomic_store_relaxed(&Handlers[SIGINT].tripped, 0);
         return 1;
     }
     return 0;
@@ -1604,11 +1608,11 @@ static void
 _clear_pending_signals(void)
 {
     int i;
-    if (!is_tripped)
+    if (!_Py_atomic_load(&is_tripped))
         return;
-    is_tripped = 0;
+    _Py_atomic_store(&is_tripped, 0);
     for (i = 1; i < NSIG; ++i) {
-        Handlers[i].tripped = 0;
+        _Py_atomic_store_relaxed(&Handlers[i].tripped, 0);
     }
 }
 
index 42aec59..a351faa 100644 (file)
@@ -1464,7 +1464,10 @@ idna_converter(PyObject *obj, struct maybe_idna *data)
         len = PyByteArray_Size(obj);
     }
     else if (PyUnicode_Check(obj)) {
-        if (PyUnicode_READY(obj) == 0 && PyUnicode_IS_COMPACT_ASCII(obj)) {
+        if (PyUnicode_READY(obj) == -1) {
+            return 0;
+        }
+        if (PyUnicode_IS_COMPACT_ASCII(obj)) {
             data->buf = PyUnicode_DATA(obj);
             len = PyUnicode_GET_LENGTH(obj);
         }
@@ -2704,7 +2707,9 @@ sock_close(PySocketSockObject *s)
         Py_BEGIN_ALLOW_THREADS
         res = SOCKETCLOSE(fd);
         Py_END_ALLOW_THREADS
-        if (res < 0) {
+        /* bpo-30319: The peer can already have closed the connection.
+           Python ignores ECONNRESET on close(). */
+        if (res < 0 && errno != ECONNRESET) {
             return s->errorhandler();
         }
     }
index 556a715..1601ec0 100644 (file)
@@ -134,6 +134,7 @@ spwd_getspnam_impl(PyObject *module, PyObject *arg)
 
     if ((bytes = PyUnicode_EncodeFSDefault(arg)) == NULL)
         return NULL;
+    /* check for embedded null bytes */
     if (PyBytes_AsStringAndSize(bytes, &name, NULL) == -1)
         goto out;
     if ((p = getspnam(name)) == NULL) {
index 328b84f..d6c34b9 100644 (file)
@@ -607,7 +607,7 @@ time_strftime(PyObject *self, PyObject *args)
         buf.tm_isdst = 1;
 
 #ifdef HAVE_WCSFTIME
-    format = PyUnicode_AsWideCharString(format_arg, NULL);
+    format = _PyUnicode_AsWideCharString(format_arg);
     if (format == NULL)
         return NULL;
     fmt = format;
index cccc033..5bf31cc 100644 (file)
@@ -580,7 +580,8 @@ zipimporter_get_data(PyObject *obj, PyObject *args)
         return NULL;
 
 #ifdef ALTSEP
-    path = _PyObject_CallMethodId(path, &PyId_replace, "CC", ALTSEP, SEP);
+    path = _PyObject_CallMethodId((PyObject *)&PyUnicode_Type, &PyId_replace,
+                                  "OCC", path, ALTSEP, SEP);
     if (!path)
         return NULL;
 #else
index 38de774..6c6c86c 100644 (file)
@@ -809,6 +809,21 @@ binary_op(PyObject *v, PyObject *w, const int op_slot, const char *op_name)
     PyObject *result = binary_op1(v, w, op_slot);
     if (result == Py_NotImplemented) {
         Py_DECREF(result);
+
+        if (op_slot == NB_SLOT(nb_rshift) &&
+            PyCFunction_Check(v) &&
+            strcmp(((PyCFunctionObject *)v)->m_ml->ml_name, "print") == 0)
+        {
+            PyErr_Format(PyExc_TypeError,
+                "unsupported operand type(s) for %.100s: "
+                "'%.100s' and '%.100s'. Did you mean \"print(<message>, "
+                "file=<output_stream>)\"?",
+                op_name,
+                v->ob_type->tp_name,
+                w->ob_type->tp_name);
+            return NULL;
+        }
+
         return binop_type_error(v, w, op_name);
     }
     return result;
index 6de697a..d45be4c 100644 (file)
@@ -22,8 +22,7 @@ all_name_chars(PyObject *o)
     static const unsigned char *name_chars = (unsigned char *)NAME_CHARS;
     const unsigned char *s, *e;
 
-    if (!PyUnicode_Check(o) || PyUnicode_READY(o) == -1 ||
-        !PyUnicode_IS_ASCII(o))
+    if (!PyUnicode_IS_ASCII(o))
         return 0;
 
     if (ok_name_char[*name_chars] == 0) {
@@ -64,6 +63,10 @@ intern_string_constants(PyObject *tuple)
     for (i = PyTuple_GET_SIZE(tuple); --i >= 0; ) {
         PyObject *v = PyTuple_GET_ITEM(tuple, i);
         if (PyUnicode_CheckExact(v)) {
+            if (PyUnicode_READY(v) == -1) {
+                PyErr_Clear();
+                continue;
+            }
             if (all_name_chars(v)) {
                 PyObject *w = v;
                 PyUnicode_InternInPlace(&v);
index b0f583a..690ef3b 100644 (file)
@@ -2006,6 +2006,8 @@ dict_dealloc(PyDictObject *mp)
     PyObject **values = mp->ma_values;
     PyDictKeysObject *keys = mp->ma_keys;
     Py_ssize_t i, n;
+
+    /* bpo-31095: UnTrack is needed before calling any callbacks */
     PyObject_GC_UnTrack(mp);
     Py_TRASHCAN_SAFE_BEGIN(mp)
     if (values != NULL) {
@@ -3432,6 +3434,8 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype)
 static void
 dictiter_dealloc(dictiterobject *di)
 {
+    /* bpo-31095: UnTrack is needed before calling any callbacks */
+    _PyObject_GC_UNTRACK(di);
     Py_XDECREF(di->di_dict);
     Py_XDECREF(di->di_result);
     PyObject_GC_Del(di);
@@ -3800,6 +3804,8 @@ dictiter_reduce(dictiterobject *di)
 static void
 dictview_dealloc(_PyDictViewObject *dv)
 {
+    /* bpo-31095: UnTrack is needed before calling any callbacks */
+    _PyObject_GC_UNTRACK(dv);
     Py_XDECREF(dv->dv_dict);
     PyObject_GC_Del(dv);
 }
index d158b97..271e293 100644 (file)
@@ -1356,11 +1356,16 @@ SyntaxError_init(PySyntaxErrorObject *self, PyObject *args, PyObject *kwds)
 
         Py_DECREF(info);
 
-        /* Issue #21669: Custom error for 'print' & 'exec' as statements */
-        if (self->text && PyUnicode_Check(self->text)) {
-            if (_report_missing_parentheses(self) < 0) {
-                return -1;
-            }
+        /*
+         * Issue #21669: Custom error for 'print' & 'exec' as statements
+         *
+         * Only applies to SyntaxError instances, not to subclasses such
+         * as TabError or IndentationError (see issue #31161)
+         */
+        if ((PyObject*)Py_TYPE(self) == PyExc_SyntaxError &&
+                self->text && PyUnicode_Check(self->text) &&
+                _report_missing_parentheses(self) < 0) {
+            return -1;
         }
     }
     return 0;
@@ -2881,6 +2886,49 @@ _PyErr_TrySetFromCause(const char *format, ...)
  * or minus, using the stream redirection syntax).
  */
 
+
+// Static helper for setting legacy print error message
+static int
+_set_legacy_print_statement_msg(PySyntaxErrorObject *self, Py_ssize_t start)
+{
+    PyObject *strip_sep_obj = PyUnicode_FromString(" \t\r\n");
+    if (strip_sep_obj == NULL)
+        return -1;
+
+    // PRINT_OFFSET is to remove `print ` word from the data.
+    const int PRINT_OFFSET = 6;
+    Py_ssize_t text_len = PyUnicode_GET_LENGTH(self->text);
+    PyObject *data = PyUnicode_Substring(self->text, PRINT_OFFSET, text_len);
+
+    if (data == NULL) {
+        Py_DECREF(strip_sep_obj);
+        return -1;
+    }
+    PyObject *new_data = _PyUnicode_XStrip(data, 2, strip_sep_obj);
+    Py_DECREF(data);
+    Py_DECREF(strip_sep_obj);
+
+    if (new_data == NULL) {
+        return -1;
+    }
+    // gets the modified text_len after stripping `print `
+    text_len = PyUnicode_GET_LENGTH(new_data);
+    const char *maybe_end_arg = "";
+    if (text_len > 0 && PyUnicode_READ_CHAR(new_data, text_len-1) == ',') {
+        maybe_end_arg = " end=\" \"";
+    }
+    PyObject *error_msg = PyUnicode_FromFormat(
+        "Missing parentheses in call to 'print'. Did you mean print(%U%s)?",
+        new_data, maybe_end_arg
+    );
+    Py_DECREF(new_data);
+    if (error_msg == NULL)
+        return -1;
+
+    Py_XSETREF(self->msg, error_msg);
+    return 1;
+}
+
 static int
 _check_for_legacy_statements(PySyntaxErrorObject *self, Py_ssize_t start)
 {
@@ -2916,9 +2964,8 @@ _check_for_legacy_statements(PySyntaxErrorObject *self, Py_ssize_t start)
     }
     if (PyUnicode_Tailmatch(self->text, print_prefix,
                             start, text_len, -1)) {
-        Py_XSETREF(self->msg,
-                  PyUnicode_FromString("Missing parentheses in call to 'print'"));
-        return 1;
+
+        return _set_legacy_print_statement_msg(self, start);
     }
 
     /* Check for legacy exec statements */
index 80bf71e..1f134aa 100644 (file)
@@ -2183,12 +2183,14 @@ _PyFloat_Pack4(double x, unsigned char *p, int le)
     }
     else {
         float y = (float)x;
-        const unsigned char *s = (unsigned char*)&y;
         int i, incr = 1;
 
         if (Py_IS_INFINITY(y) && !Py_IS_INFINITY(x))
             goto Overflow;
 
+        unsigned char s[sizeof(float)];
+        memcpy(s, &y, sizeof(float));
+
         if ((float_format == ieee_little_endian_format && !le)
             || (float_format == ieee_big_endian_format && le)) {
             p += 3;
@@ -2196,7 +2198,7 @@ _PyFloat_Pack4(double x, unsigned char *p, int le)
         }
 
         for (i = 0; i < 4; i++) {
-            *p = *s++;
+            *p = s[i];
             p += incr;
         }
         return 0;
index f9f1bf3..65f90e8 100644 (file)
@@ -875,8 +875,7 @@ odict_eq(PyObject *a, PyObject *b)
 
 PyDoc_STRVAR(odict_init__doc__,
 "Initialize an ordered dictionary.  The signature is the same as\n\
-        regular dictionaries, but keyword arguments are not recommended because\n\
-        their insertion order is arbitrary.\n\
+        regular dictionaries.  Keyword argument order is preserved.\n\
 \n\
         ");
 
@@ -1155,10 +1154,12 @@ _odict_popkey(PyObject *od, PyObject *key, PyObject *failobj)
 /* popitem() */
 
 PyDoc_STRVAR(odict_popitem__doc__,
-"od.popitem() -> (k, v), return and remove a (key, value) pair.\n\
-        Pairs are returned in LIFO order if last is true or FIFO order if false.\n\
-\n\
-        ");
+"popitem($self, /, last=True)\n"
+"--\n"
+"\n"
+"Remove and return a (key, value) pair from the dictionary.\n"
+"\n"
+"Pairs are returned in LIFO order if last is true or FIFO order if false.");
 
 static PyObject *
 odict_popitem(PyObject *od, PyObject *args, PyObject *kwargs)
index c1bc1e1..24272b4 100644 (file)
@@ -556,6 +556,7 @@ set_dealloc(PySetObject *so)
     setentry *entry;
     Py_ssize_t used = so->used;
 
+    /* bpo-31095: UnTrack is needed before calling any callbacks */
     PyObject_GC_UnTrack(so);
     Py_TRASHCAN_SAFE_BEGIN(so)
     if (so->weakreflist != NULL)
@@ -812,6 +813,8 @@ typedef struct {
 static void
 setiter_dealloc(setiterobject *si)
 {
+    /* bpo-31095: UnTrack is needed before calling any callbacks */
+    _PyObject_GC_UNTRACK(si);
     Py_XDECREF(si->si_set);
     PyObject_GC_Del(si);
 }
index 43f2f32..f019d9a 100644 (file)
@@ -330,7 +330,7 @@ STRINGLIB(utf8_encoder)(PyObject *unicode,
             case _Py_ERROR_REPLACE:
                 memset(p, '?', endpos - startpos);
                 p += (endpos - startpos);
-                /* fall through the ignore handler */
+                /* fall through */
             case _Py_ERROR_IGNORE:
                 i += (endpos - startpos - 1);
                 break;
@@ -378,7 +378,7 @@ STRINGLIB(utf8_encoder)(PyObject *unicode,
                 }
                 startpos = k;
                 assert(startpos < endpos);
-                /* fall through the default handler */
+                /* fall through */
             default:
                 rep = unicode_encode_call_errorhandler(
                       errors, &error_handler_obj, "utf-8", "surrogates not allowed",
index 14fa28e..7314af0 100644 (file)
@@ -412,18 +412,22 @@ get_field_object(SubString *input, PyObject *args, PyObject *kwargs,
     if (index == -1) {
         /* look up in kwargs */
         PyObject *key = SubString_new_object(&first);
-        if (key == NULL)
+        if (key == NULL) {
             goto error;
-
-        /* Use PyObject_GetItem instead of PyDict_GetItem because this
-           code is no longer just used with kwargs. It might be passed
-           a non-dict when called through format_map. */
-        if ((kwargs == NULL) || (obj = PyObject_GetItem(kwargs, key)) == NULL) {
+        }
+        if (kwargs == NULL) {
             PyErr_SetObject(PyExc_KeyError, key);
             Py_DECREF(key);
             goto error;
         }
+        /* Use PyObject_GetItem instead of PyDict_GetItem because this
+           code is no longer just used with kwargs. It might be passed
+           a non-dict when called through format_map. */
+        obj = PyObject_GetItem(kwargs, key);
         Py_DECREF(key);
+        if (obj == NULL) {
+            goto error;
+        }
     }
     else {
         /* If args is NULL, we have a format string with a positional field
index 271b935..cb5e235 100644 (file)
@@ -22,9 +22,9 @@
 #define MCACHE_HASH_METHOD(type, name)                                  \
         MCACHE_HASH((type)->tp_version_tag,                     \
                     ((PyASCIIObject *)(name))->hash)
-#define MCACHE_CACHEABLE_NAME(name)                                     \
-        PyUnicode_CheckExact(name) &&                            \
-        PyUnicode_READY(name) != -1 &&                      \
+#define MCACHE_CACHEABLE_NAME(name)                             \
+        PyUnicode_CheckExact(name) &&                           \
+        PyUnicode_IS_READY(name) &&                             \
         PyUnicode_GET_LENGTH(name) <= MCACHE_MAX_ATTR_SIZE
 
 struct method_cache_entry {
index 4e0c663..e9fc658 100644 (file)
@@ -1779,6 +1779,7 @@ unicode_dealloc(PyObject *unicode)
 
     case SSTATE_INTERNED_IMMORTAL:
         Py_FatalError("Immortal interned string died.");
+        /* fall through */
 
     default:
         Py_FatalError("Inconsistent interned string state.");
@@ -3017,6 +3018,37 @@ PyUnicode_AsWideCharString(PyObject *unicode,
     return buffer;
 }
 
+wchar_t*
+_PyUnicode_AsWideCharString(PyObject *unicode)
+{
+    const wchar_t *wstr;
+    wchar_t *buffer;
+    Py_ssize_t buflen;
+
+    if (unicode == NULL) {
+        PyErr_BadInternalCall();
+        return NULL;
+    }
+
+    wstr = PyUnicode_AsUnicodeAndSize(unicode, &buflen);
+    if (wstr == NULL) {
+        return NULL;
+    }
+    if (wcslen(wstr) != (size_t)buflen) {
+        PyErr_SetString(PyExc_ValueError,
+                        "embedded null character");
+        return NULL;
+    }
+
+    buffer = PyMem_NEW(wchar_t, buflen + 1);
+    if (buffer == NULL) {
+        PyErr_NoMemory();
+        return NULL;
+    }
+    memcpy(buffer, wstr, (buflen + 1) * sizeof(wchar_t));
+    return buffer;
+}
+
 #endif /* HAVE_WCHAR_H */
 
 PyObject *
@@ -4133,6 +4165,20 @@ PyUnicode_AsUnicode(PyObject *unicode)
     return PyUnicode_AsUnicodeAndSize(unicode, NULL);
 }
 
+const Py_UNICODE *
+_PyUnicode_AsUnicode(PyObject *unicode)
+{
+    Py_ssize_t size;
+    const Py_UNICODE *wstr;
+
+    wstr = PyUnicode_AsUnicodeAndSize(unicode, &size);
+    if (wstr && wcslen(wstr) != (size_t)size) {
+        PyErr_SetString(PyExc_ValueError, "embedded null character");
+        return NULL;
+    }
+    return wstr;
+}
+
 
 Py_ssize_t
 PyUnicode_GetSize(PyObject *unicode)
@@ -4165,10 +4211,13 @@ PyUnicode_ReadChar(PyObject *unicode, Py_ssize_t index)
     void *data;
     int kind;
 
-    if (!PyUnicode_Check(unicode) || PyUnicode_READY(unicode) == -1) {
+    if (!PyUnicode_Check(unicode)) {
         PyErr_BadArgument();
         return (Py_UCS4)-1;
     }
+    if (PyUnicode_READY(unicode) == -1) {
+        return (Py_UCS4)-1;
+    }
     if (index < 0 || index >= PyUnicode_GET_LENGTH(unicode)) {
         PyErr_SetString(PyExc_IndexError, "string index out of range");
         return (Py_UCS4)-1;
@@ -5468,13 +5517,12 @@ _PyUnicode_EncodeUTF32(PyObject *str,
         /* four bytes are reserved for each surrogate */
         if (moreunits > 1) {
             Py_ssize_t outpos = out - (uint32_t*) PyBytes_AS_STRING(v);
-            Py_ssize_t morebytes = 4 * (moreunits - 1);
-            if (PyBytes_GET_SIZE(v) > PY_SSIZE_T_MAX - morebytes) {
+            if (moreunits >= (PY_SSIZE_T_MAX - PyBytes_GET_SIZE(v)) / 4) {
                 /* integer overflow */
                 PyErr_NoMemory();
                 goto error;
             }
-            if (_PyBytes_Resize(&v, PyBytes_GET_SIZE(v) + morebytes) < 0)
+            if (_PyBytes_Resize(&v, PyBytes_GET_SIZE(v) + 4 * (moreunits - 1)) < 0)
                 goto error;
             out = (uint32_t*) PyBytes_AS_STRING(v) + outpos;
         }
@@ -5820,13 +5868,12 @@ _PyUnicode_EncodeUTF16(PyObject *str,
         /* two bytes are reserved for each surrogate */
         if (moreunits > 1) {
             Py_ssize_t outpos = out - (unsigned short*) PyBytes_AS_STRING(v);
-            Py_ssize_t morebytes = 2 * (moreunits - 1);
-            if (PyBytes_GET_SIZE(v) > PY_SSIZE_T_MAX - morebytes) {
+            if (moreunits >= (PY_SSIZE_T_MAX - PyBytes_GET_SIZE(v)) / 2) {
                 /* integer overflow */
                 PyErr_NoMemory();
                 goto error;
             }
-            if (_PyBytes_Resize(&v, PyBytes_GET_SIZE(v) + morebytes) < 0)
+            if (_PyBytes_Resize(&v, PyBytes_GET_SIZE(v) + 2 * (moreunits - 1)) < 0)
                 goto error;
             out = (unsigned short*) PyBytes_AS_STRING(v) + outpos;
         }
@@ -6506,6 +6553,10 @@ _PyUnicode_DecodeUnicodeInternal(const char *s,
                      1))
         return NULL;
 
+    if (size < 0) {
+        PyErr_BadInternalCall();
+        return NULL;
+    }
     if (size == 0)
         _Py_RETURN_UNICODE_EMPTY();
 
@@ -6766,7 +6817,7 @@ unicode_encode_ucs1(PyObject *unicode,
             case _Py_ERROR_REPLACE:
                 memset(str, '?', collend - collstart);
                 str += (collend - collstart);
-                /* fall through ignore error handler */
+                /* fall through */
             case _Py_ERROR_IGNORE:
                 pos = collend;
                 break;
@@ -6805,7 +6856,7 @@ unicode_encode_ucs1(PyObject *unicode,
                     break;
                 collstart = pos;
                 assert(collstart != collend);
-                /* fallback to general error handling */
+                /* fall through */
 
             default:
                 rep = unicode_encode_call_errorhandler(errors, &error_handler_obj,
@@ -7307,6 +7358,10 @@ decode_code_page_stateful(int code_page,
         PyErr_SetString(PyExc_ValueError, "invalid code page number");
         return NULL;
     }
+    if (size < 0) {
+        PyErr_BadInternalCall();
+        return NULL;
+    }
 
     if (consumed)
         *consumed = 0;
@@ -11655,10 +11710,13 @@ unicode_getitem(PyObject *self, Py_ssize_t index)
     enum PyUnicode_Kind kind;
     Py_UCS4 ch;
 
-    if (!PyUnicode_Check(self) || PyUnicode_READY(self) == -1) {
+    if (!PyUnicode_Check(self)) {
         PyErr_BadArgument();
         return NULL;
     }
+    if (PyUnicode_READY(self) == -1) {
+        return NULL;
+    }
     if (index < 0 || index >= PyUnicode_GET_LENGTH(self)) {
         PyErr_SetString(PyExc_IndexError, "string index out of range");
         return NULL;
diff --git a/PC/_findvs.cpp b/PC/_findvs.cpp
new file mode 100644 (file)
index 0000000..dd7c1fc
--- /dev/null
@@ -0,0 +1,263 @@
+//
+// Helper library for location Visual Studio installations
+// using the COM-based query API.
+//
+// Copyright (c) Microsoft Corporation
+// Licensed to PSF under a contributor agreement
+//
+
+// Version history
+//  2017-05: Initial contribution (Steve Dower)
+
+#include <Windows.h>
+#include <Strsafe.h>
+#include "external\include\Setup.Configuration.h"
+#pragma comment(lib, "ole32.lib")
+#pragma comment(lib, "oleaut32.lib")
+#pragma comment(lib, "version.lib")
+#pragma comment(lib, "Microsoft.VisualStudio.Setup.Configuration.Native.lib")
+
+#include <Python.h>
+
+static PyObject *error_from_hr(HRESULT hr)
+{
+    if (FAILED(hr))
+        PyErr_Format(PyExc_OSError, "Error %08x", hr);
+    assert(PyErr_Occurred());
+    return nullptr;
+}
+
+static PyObject *get_install_name(ISetupInstance2 *inst)
+{
+    HRESULT hr;
+    BSTR name;
+    PyObject *str = nullptr;
+    if (FAILED(hr = inst->GetDisplayName(LOCALE_USER_DEFAULT, &name)))
+        goto error;
+    str = PyUnicode_FromWideChar(name, SysStringLen(name));
+    SysFreeString(name);
+    return str;
+error:
+
+    return error_from_hr(hr);
+}
+
+static PyObject *get_install_version(ISetupInstance *inst)
+{
+    HRESULT hr;
+    BSTR ver;
+    PyObject *str = nullptr;
+    if (FAILED(hr = inst->GetInstallationVersion(&ver)))
+        goto error;
+    str = PyUnicode_FromWideChar(ver, SysStringLen(ver));
+    SysFreeString(ver);
+    return str;
+error:
+
+    return error_from_hr(hr);
+}
+
+static PyObject *get_install_path(ISetupInstance *inst)
+{
+    HRESULT hr;
+    BSTR path;
+    PyObject *str = nullptr;
+    if (FAILED(hr = inst->GetInstallationPath(&path)))
+        goto error;
+    str = PyUnicode_FromWideChar(path, SysStringLen(path));
+    SysFreeString(path);
+    return str;
+error:
+
+    return error_from_hr(hr);
+}
+
+static PyObject *get_installed_packages(ISetupInstance2 *inst)
+{
+    HRESULT hr;
+    PyObject *res = nullptr;
+    LPSAFEARRAY sa_packages = nullptr;
+    LONG ub = 0;
+    IUnknown **packages = nullptr;
+    PyObject *str = nullptr;
+
+    if (FAILED(hr = inst->GetPackages(&sa_packages)) ||
+        FAILED(hr = SafeArrayAccessData(sa_packages, (void**)&packages)) ||
+        FAILED(SafeArrayGetUBound(sa_packages, 1, &ub)) ||
+        !(res = PyList_New(0)))
+        goto error;
+
+    for (LONG i = 0; i < ub; ++i) {
+        ISetupPackageReference *package = nullptr;
+        BSTR id = nullptr;
+        PyObject *str = nullptr;
+
+        if (FAILED(hr = packages[i]->QueryInterface(&package)) ||
+            FAILED(hr = package->GetId(&id)))
+            goto iter_error;
+
+        str = PyUnicode_FromWideChar(id, SysStringLen(id));
+        SysFreeString(id);
+
+        if (!str || PyList_Append(res, str) < 0)
+            goto iter_error;
+
+        Py_CLEAR(str);
+        package->Release();
+        continue;
+
+    iter_error:
+        if (package) package->Release();
+        Py_XDECREF(str);
+
+        goto error;
+    }
+
+    SafeArrayUnaccessData(sa_packages);
+    SafeArrayDestroy(sa_packages);
+
+    return res;
+error:
+    if (sa_packages && packages) SafeArrayUnaccessData(sa_packages);
+    if (sa_packages) SafeArrayDestroy(sa_packages);
+    Py_XDECREF(res);
+
+    return error_from_hr(hr);
+}
+
+static PyObject *find_all_instances()
+{
+    ISetupConfiguration *sc = nullptr;
+    ISetupConfiguration2 *sc2 = nullptr;
+    IEnumSetupInstances *enm = nullptr;
+    ISetupInstance *inst = nullptr;
+    ISetupInstance2 *inst2 = nullptr;
+    PyObject *res = nullptr;
+    ULONG fetched;
+    HRESULT hr;
+
+    if (!(res = PyList_New(0)))
+        goto error;
+
+    if (FAILED(hr = CoCreateInstance(
+        __uuidof(SetupConfiguration),
+        NULL,
+        CLSCTX_INPROC_SERVER,
+        __uuidof(ISetupConfiguration),
+        (LPVOID*)&sc
+    )) && hr != REGDB_E_CLASSNOTREG)
+        goto error;
+
+    // If the class is not registered, there are no VS instances installed
+    if (hr == REGDB_E_CLASSNOTREG)
+        return res;
+
+    if (FAILED(hr = sc->QueryInterface(&sc2)) ||
+        FAILED(hr = sc2->EnumAllInstances(&enm)))
+        goto error;
+
+    while (SUCCEEDED(enm->Next(1, &inst, &fetched)) && fetched) {
+        PyObject *name = nullptr;
+        PyObject *version = nullptr;
+        PyObject *path = nullptr;
+        PyObject *packages = nullptr;
+        PyObject *tuple = nullptr;
+
+        if (FAILED(hr = inst->QueryInterface(&inst2)) ||
+            !(name = get_install_name(inst2)) ||
+            !(version = get_install_version(inst)) ||
+            !(path = get_install_path(inst)) ||
+            !(packages = get_installed_packages(inst2)) ||
+            !(tuple = PyTuple_Pack(4, name, version, path, packages)) ||
+            PyList_Append(res, tuple) < 0)
+            goto iter_error;
+
+        Py_DECREF(tuple);
+        Py_DECREF(packages);
+        Py_DECREF(path);
+        Py_DECREF(version);
+        Py_DECREF(name);
+        continue;
+    iter_error:
+        if (inst2) inst2->Release();
+        Py_XDECREF(tuple);
+        Py_XDECREF(packages);
+        Py_XDECREF(path);
+        Py_XDECREF(version);
+        Py_XDECREF(name);
+        goto error;
+    }
+
+    enm->Release();
+    sc2->Release();
+    sc->Release();
+    return res;
+
+error:
+    if (enm) enm->Release();
+    if (sc2) sc2->Release();
+    if (sc) sc->Release();
+    Py_XDECREF(res);
+
+    return error_from_hr(hr);
+}
+
+PyDoc_STRVAR(findvs_findall_doc, "findall()\
+\
+Finds all installed versions of Visual Studio.\
+\
+This function will initialize COM temporarily. To avoid impact on other parts\
+of your application, use a new thread to make this call.");
+
+static PyObject *findvs_findall(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+    HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
+    if (hr == RPC_E_CHANGED_MODE)
+        hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
+    if (FAILED(hr))
+        return error_from_hr(hr);
+    PyObject *res = find_all_instances();
+    CoUninitialize();
+    return res;
+}
+
+// List of functions to add to findvs in exec_findvs().
+static PyMethodDef findvs_functions[] = {
+    { "findall", (PyCFunction)findvs_findall, METH_VARARGS | METH_KEYWORDS, findvs_findall_doc },
+    { NULL, NULL, 0, NULL }
+};
+
+// Initialize findvs. May be called multiple times, so avoid
+// using static state.
+static int exec_findvs(PyObject *module)
+{
+    PyModule_AddFunctions(module, findvs_functions);
+
+    return 0; // success
+}
+
+PyDoc_STRVAR(findvs_doc, "The _findvs helper module");
+
+static PyModuleDef_Slot findvs_slots[] = {
+    { Py_mod_exec, exec_findvs },
+    { 0, NULL }
+};
+
+static PyModuleDef findvs_def = {
+    PyModuleDef_HEAD_INIT,
+    "_findvs",
+    findvs_doc,
+    0,              // m_size
+    NULL,           // m_methods
+    findvs_slots,
+    NULL,           // m_traverse
+    NULL,           // m_clear
+    NULL,           // m_free
+};
+
+extern "C" {
+    PyMODINIT_FUNC PyInit__findvs(void)
+    {
+        return PyModuleDef_Init(&findvs_def);
+    }
+}
\ No newline at end of file
index 789b04f..15aa7d8 100644 (file)
--- a/PC/_msi.c
+++ b/PC/_msi.c
@@ -600,8 +600,12 @@ summary_setproperty(msiobj* si, PyObject *args)
         return NULL;
 
     if (PyUnicode_Check(data)) {
+        const WCHAR *value = _PyUnicode_AsUnicode(data);
+        if (value == NULL) {
+            return NULL;
+        }
         status = MsiSummaryInfoSetPropertyW(si->h, field, VT_LPSTR,
-            0, NULL, PyUnicode_AsUnicode(data));
+            0, NULL, value);
     } else if (PyLong_CheckExact(data)) {
         long value = PyLong_AsLong(data);
         if (value == -1 && PyErr_Occurred()) {
index 43d9e20..f631d73 100644 (file)
@@ -1,7 +1,7 @@
 /* Module configuration */
 
 /* This file contains the table of built-in modules.
-   See create_builtin() in import.c. */
+    See create_builtin() in import.c. */
 
 #include "Python.h"
 
@@ -69,6 +69,7 @@ extern PyObject* _PyWarnings_Init(void);
 extern PyObject* PyInit__string(void);
 extern PyObject* PyInit__stat(void);
 extern PyObject* PyInit__opcode(void);
+extern PyObject* PyInit__findvs(void);
 
 /* tools/freeze/makeconfig.py marker for additional "extern" */
 /* -- ADDMODULE MARKER 1 -- */
@@ -165,6 +166,8 @@ struct _inittab _PyImport_Inittab[] = {
     {"_stat", PyInit__stat},
     {"_opcode", PyInit__opcode},
 
+    {"_findvs", PyInit__findvs},
+
     /* Sentinel */
     {0, 0}
 };
diff --git a/PC/external/Externals.txt b/PC/external/Externals.txt
new file mode 100644 (file)
index 0000000..618fe16
--- /dev/null
@@ -0,0 +1,3 @@
+The files in this folder are from the Microsoft.VisualStudio.Setup.Configuration.Native package on Nuget.
+
+They are licensed under the MIT license.
diff --git a/PC/external/include/Setup.Configuration.h b/PC/external/include/Setup.Configuration.h
new file mode 100644 (file)
index 0000000..1fb3187
--- /dev/null
@@ -0,0 +1,827 @@
+// The MIT License(MIT)
+// Copyright(C) Microsoft Corporation.All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+
+#pragma once
+
+// Constants
+//
+#ifndef E_NOTFOUND
+#define E_NOTFOUND HRESULT_FROM_WIN32(ERROR_NOT_FOUND)
+#endif
+
+#ifndef E_FILENOTFOUND
+#define E_FILENOTFOUND HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)
+#endif
+
+#ifndef E_NOTSUPPORTED
+#define E_NOTSUPPORTED HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)
+#endif
+
+// Enumerations
+//
+/// <summary>
+/// The state of an instance.
+/// </summary>
+enum InstanceState
+{
+    /// <summary>
+    /// The instance state has not been determined.
+    /// </summary>
+    eNone = 0,
+
+    /// <summary>
+    /// The instance installation path exists.
+    /// </summary>
+    eLocal = 1,
+
+    /// <summary>
+    /// A product is registered to the instance.
+    /// </summary>
+    eRegistered = 2,
+
+    /// <summary>
+    /// No reboot is required for the instance.
+    /// </summary>
+    eNoRebootRequired = 4,
+
+    /// <summary>
+    /// No errors were reported for the instance.
+    /// </summary>
+    eNoErrors = 8,
+
+    /// <summary>
+    /// The instance represents a complete install.
+    /// </summary>
+    eComplete = MAXUINT,
+};
+
+// Forward interface declarations
+//
+#ifndef __ISetupInstance_FWD_DEFINED__
+#define __ISetupInstance_FWD_DEFINED__
+typedef struct ISetupInstance ISetupInstance;
+#endif
+
+#ifndef __ISetupInstance2_FWD_DEFINED__
+#define __ISetupInstance2_FWD_DEFINED__
+typedef struct ISetupInstance2 ISetupInstance2;
+#endif
+
+#ifndef __ISetupLocalizedProperties_FWD_DEFINED__
+#define __ISetupLocalizedProperties_FWD_DEFINED__
+typedef struct ISetupLocalizedProperties ISetupLocalizedProperties;
+#endif
+
+#ifndef __IEnumSetupInstances_FWD_DEFINED__
+#define __IEnumSetupInstances_FWD_DEFINED__
+typedef struct IEnumSetupInstances IEnumSetupInstances;
+#endif
+
+#ifndef __ISetupConfiguration_FWD_DEFINED__
+#define __ISetupConfiguration_FWD_DEFINED__
+typedef struct ISetupConfiguration ISetupConfiguration;
+#endif
+
+#ifndef __ISetupConfiguration2_FWD_DEFINED__
+#define __ISetupConfiguration2_FWD_DEFINED__
+typedef struct ISetupConfiguration2 ISetupConfiguration2;
+#endif
+
+#ifndef __ISetupPackageReference_FWD_DEFINED__
+#define __ISetupPackageReference_FWD_DEFINED__
+typedef struct ISetupPackageReference ISetupPackageReference;
+#endif
+
+#ifndef __ISetupHelper_FWD_DEFINED__
+#define __ISetupHelper_FWD_DEFINED__
+typedef struct ISetupHelper ISetupHelper;
+#endif
+
+#ifndef __ISetupErrorState_FWD_DEFINED__
+#define __ISetupErrorState_FWD_DEFINED__
+typedef struct ISetupErrorState ISetupErrorState;
+#endif
+
+#ifndef __ISetupErrorState2_FWD_DEFINED__
+#define __ISetupErrorState2_FWD_DEFINED__
+typedef struct ISetupErrorState2 ISetupErrorState2;
+#endif
+
+#ifndef __ISetupFailedPackageReference_FWD_DEFINED__
+#define __ISetupFailedPackageReference_FWD_DEFINED__
+typedef struct ISetupFailedPackageReference ISetupFailedPackageReference;
+#endif
+
+#ifndef __ISetupFailedPackageReference2_FWD_DEFINED__
+#define __ISetupFailedPackageReference2_FWD_DEFINED__
+typedef struct ISetupFailedPackageReference2 ISetupFailedPackageReference2;
+#endif
+
+#ifndef __ISetupPropertyStore_FWD_DEFINED__
+#define __ISetupPropertyStore_FWD_DEFINED__
+typedef struct ISetupPropertyStore ISetupPropertyStore;
+#endif
+
+#ifndef __ISetupLocalizedPropertyStore_FWD_DEFINED__
+#define __ISetupLocalizedPropertyStore_FWD_DEFINED__
+typedef struct ISetupLocalizedPropertyStore ISetupLocalizedPropertyStore;
+#endif
+
+// Forward class declarations
+//
+#ifndef __SetupConfiguration_FWD_DEFINED__
+#define __SetupConfiguration_FWD_DEFINED__
+
+#ifdef __cplusplus
+typedef class SetupConfiguration SetupConfiguration;
+#endif
+
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Interface definitions
+//
+EXTERN_C const IID IID_ISetupInstance;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+/// <summary>
+/// Information about an instance of a product.
+/// </summary>
+struct DECLSPEC_UUID("B41463C3-8866-43B5-BC33-2B0676F7F42E") DECLSPEC_NOVTABLE ISetupInstance : public IUnknown
+{
+    /// <summary>
+    /// Gets the instance identifier (should match the name of the parent instance directory).
+    /// </summary>
+    /// <param name="pbstrInstanceId">The instance identifier.</param>
+    /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist.</returns>
+    STDMETHOD(GetInstanceId)(
+        _Out_ BSTR* pbstrInstanceId
+        ) = 0;
+
+    /// <summary>
+    /// Gets the local date and time when the installation was originally installed.
+    /// </summary>
+    /// <param name="pInstallDate">The local date and time when the installation was originally installed.</param>
+    /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the property is not defined.</returns>
+    STDMETHOD(GetInstallDate)(
+        _Out_ LPFILETIME pInstallDate
+        ) = 0;
+
+    /// <summary>
+    /// Gets the unique name of the installation, often indicating the branch and other information used for telemetry.
+    /// </summary>
+    /// <param name="pbstrInstallationName">The unique name of the installation, often indicating the branch and other information used for telemetry.</param>
+    /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the property is not defined.</returns>
+    STDMETHOD(GetInstallationName)(
+        _Out_ BSTR* pbstrInstallationName
+        ) = 0;
+
+    /// <summary>
+    /// Gets the path to the installation root of the product.
+    /// </summary>
+    /// <param name="pbstrInstallationPath">The path to the installation root of the product.</param>
+    /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the property is not defined.</returns>
+    STDMETHOD(GetInstallationPath)(
+        _Out_ BSTR* pbstrInstallationPath
+        ) = 0;
+
+    /// <summary>
+    /// Gets the version of the product installed in this instance.
+    /// </summary>
+    /// <param name="pbstrInstallationVersion">The version of the product installed in this instance.</param>
+    /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the property is not defined.</returns>
+    STDMETHOD(GetInstallationVersion)(
+        _Out_ BSTR* pbstrInstallationVersion
+        ) = 0;
+
+    /// <summary>
+    /// Gets the display name (title) of the product installed in this instance.
+    /// </summary>
+    /// <param name="lcid">The LCID for the display name.</param>
+    /// <param name="pbstrDisplayName">The display name (title) of the product installed in this instance.</param>
+    /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the property is not defined.</returns>
+    STDMETHOD(GetDisplayName)(
+        _In_ LCID lcid,
+        _Out_ BSTR* pbstrDisplayName
+        ) = 0;
+
+    /// <summary>
+    /// Gets the description of the product installed in this instance.
+    /// </summary>
+    /// <param name="lcid">The LCID for the description.</param>
+    /// <param name="pbstrDescription">The description of the product installed in this instance.</param>
+    /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the property is not defined.</returns>
+    STDMETHOD(GetDescription)(
+        _In_ LCID lcid,
+        _Out_ BSTR* pbstrDescription
+        ) = 0;
+
+    /// <summary>
+    /// Resolves the optional relative path to the root path of the instance.
+    /// </summary>
+    /// <param name="pwszRelativePath">A relative path within the instance to resolve, or NULL to get the root path.</param>
+    /// <param name="pbstrAbsolutePath">The full path to the optional relative path within the instance. If the relative path is NULL, the root path will always terminate in a backslash.</param>
+    /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the property is not defined.</returns>
+    STDMETHOD(ResolvePath)(
+        _In_opt_z_ LPCOLESTR pwszRelativePath,
+        _Out_ BSTR* pbstrAbsolutePath
+        ) = 0;
+};
+#endif
+
+EXTERN_C const IID IID_ISetupInstance2;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+/// <summary>
+/// Information about an instance of a product.
+/// </summary>
+struct DECLSPEC_UUID("89143C9A-05AF-49B0-B717-72E218A2185C") DECLSPEC_NOVTABLE ISetupInstance2 : public ISetupInstance
+{
+    /// <summary>
+    /// Gets the state of the instance.
+    /// </summary>
+    /// <param name="pState">The state of the instance.</param>
+    /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist.</returns>
+    STDMETHOD(GetState)(
+        _Out_ InstanceState* pState
+        ) = 0;
+
+    /// <summary>
+    /// Gets an array of package references registered to the instance.
+    /// </summary>
+    /// <param name="ppsaPackages">Pointer to an array of <see cref="ISetupPackageReference"/>.</param>
+    /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the packages property is not defined.</returns>
+    STDMETHOD(GetPackages)(
+        _Out_ LPSAFEARRAY* ppsaPackages
+        ) = 0;
+
+    /// <summary>
+    /// Gets a pointer to the <see cref="ISetupPackageReference"/> that represents the registered product.
+    /// </summary>
+    /// <param name="ppPackage">Pointer to an instance of <see cref="ISetupPackageReference"/>. This may be NULL if <see cref="GetState"/> does not return <see cref="eComplete"/>.</param>
+    /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist and E_NOTFOUND if the packages property is not defined.</returns>
+    STDMETHOD(GetProduct)(
+        _Outptr_result_maybenull_ ISetupPackageReference** ppPackage
+        ) = 0;
+
+    /// <summary>
+    /// Gets the relative path to the product application, if available.
+    /// </summary>
+    /// <param name="pbstrProductPath">The relative path to the product application, if available.</param>
+    /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist.</returns>
+    STDMETHOD(GetProductPath)(
+        _Outptr_result_maybenull_ BSTR* pbstrProductPath
+        ) = 0;
+
+    /// <summary>
+    /// Gets the error state of the instance, if available.
+    /// </summary>
+    /// <param name="pErrorState">The error state of the instance, if available.</param>
+    /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist.</returns>
+    STDMETHOD(GetErrors)(
+        _Outptr_result_maybenull_ ISetupErrorState** ppErrorState
+        ) = 0;
+
+    /// <summary>
+    /// Gets a value indicating whether the instance can be launched.
+    /// </summary>
+    /// <param name="pfIsLaunchable">Whether the instance can be launched.</param>
+    /// <returns>Standard HRESULT indicating success or failure.</returns>
+    /// <remarks>
+    /// An instance could have had errors during install but still be launched. Some features may not work correctly, but others will.
+    /// </remarks>
+    STDMETHOD(IsLaunchable)(
+        _Out_ VARIANT_BOOL* pfIsLaunchable
+        ) = 0;
+
+    /// <summary>
+    /// Gets a value indicating whether the instance is complete.
+    /// </summary>
+    /// <param name="pfIsLaunchable">Whether the instance is complete.</param>
+    /// <returns>Standard HRESULT indicating success or failure.</returns>
+    /// <remarks>
+    /// An instance is complete if it had no errors during install, resume, or repair.
+    /// </remarks>
+    STDMETHOD(IsComplete)(
+        _Out_ VARIANT_BOOL* pfIsComplete
+        ) = 0;
+
+    /// <summary>
+    /// Gets product-specific properties.
+    /// </summary>
+    /// <param name="ppPropeties">A pointer to an instance of <see cref="ISetupPropertyStore"/>. This may be NULL if no properties are defined.</param>
+    /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist.</returns>
+    STDMETHOD(GetProperties)(
+        _Outptr_result_maybenull_ ISetupPropertyStore** ppProperties
+        ) = 0;
+
+    /// <summary>
+    /// Gets the directory path to the setup engine that installed the instance.
+    /// </summary>
+    /// <param name="pbstrEnginePath">The directory path to the setup engine that installed the instance.</param>
+    /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist.</returns>
+    STDMETHOD(GetEnginePath)(
+        _Outptr_result_maybenull_ BSTR* pbstrEnginePath
+        ) = 0;
+};
+#endif
+
+EXTERN_C const IID IID_ISetupLocalizedProperties;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+/// <summary>
+/// Provides localized properties of an instance of a product.
+/// </summary>
+struct DECLSPEC_UUID("F4BD7382-FE27-4AB4-B974-9905B2A148B0") DECLSPEC_NOVTABLE ISetupLocalizedProperties : public IUnknown
+{
+    /// <summary>
+    /// Gets localized product-specific properties.
+    /// </summary>
+    /// <param name="ppLocalizedProperties">A pointer to an instance of <see cref="ISetupLocalizedPropertyStore"/>. This may be NULL if no properties are defined.</param>
+    /// <returns>Standard HRESULT indicating success or failure.</returns>
+    STDMETHOD(GetLocalizedProperties)(
+        _Outptr_result_maybenull_ ISetupLocalizedPropertyStore** ppLocalizedProperties
+    ) = 0;
+
+    /// <summary>
+    /// Gets localized channel-specific properties.
+    /// </summary>
+    /// <param name="ppLocalizedChannelProperties">A pointer to an instance of <see cref="ISetupLocalizedPropertyStore"/>. This may be NULL if no channel properties are defined.</param>
+    /// <returns>Standard HRESULT indicating success or failure.</returns>
+    STDMETHOD(GetLocalizedChannelProperties)(
+        _Outptr_result_maybenull_ ISetupLocalizedPropertyStore** ppLocalizedChannelProperties
+    ) = 0;
+};
+#endif
+
+EXTERN_C const IID IID_IEnumSetupInstances;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+/// <summary>
+/// An enumerator of installed <see cref="ISetupInstance"/> objects.
+/// </summary>
+struct DECLSPEC_UUID("6380BCFF-41D3-4B2E-8B2E-BF8A6810C848") DECLSPEC_NOVTABLE IEnumSetupInstances : public IUnknown
+{
+    /// <summary>
+    /// Retrieves the next set of product instances in the enumeration sequence.
+    /// </summary>
+    /// <param name="celt">The number of product instances to retrieve.</param>
+    /// <param name="rgelt">A pointer to an array of <see cref="ISetupInstance"/>.</param>
+    /// <param name="pceltFetched">A pointer to the number of product instances retrieved. If <paramref name="celt"/> is 1 this parameter may be NULL.</param>
+    /// <returns>S_OK if the number of elements were fetched, S_FALSE if nothing was fetched (at end of enumeration), E_INVALIDARG if <paramref name="celt"/> is greater than 1 and pceltFetched is NULL, or E_OUTOFMEMORY if an <see cref="ISetupInstance"/> could not be allocated.</returns>
+    STDMETHOD(Next)(
+        _In_ ULONG celt,
+        _Out_writes_to_(celt, *pceltFetched) ISetupInstance** rgelt,
+        _Out_opt_ _Deref_out_range_(0, celt) ULONG* pceltFetched
+        ) = 0;
+
+    /// <summary>
+    /// Skips the next set of product instances in the enumeration sequence.
+    /// </summary>
+    /// <param name="celt">The number of product instances to skip.</param>
+    /// <returns>S_OK if the number of elements could be skipped; otherwise, S_FALSE;</returns>
+    STDMETHOD(Skip)(
+        _In_ ULONG celt
+        ) = 0;
+
+    /// <summary>
+    /// Resets the enumeration sequence to the beginning.
+    /// </summary>
+    /// <returns>Always returns S_OK;</returns>
+    STDMETHOD(Reset)(void) = 0;
+
+    /// <summary>
+    /// Creates a new enumeration object in the same state as the current enumeration object: the new object points to the same place in the enumeration sequence.
+    /// </summary>
+    /// <param name="ppenum">A pointer to a pointer to a new <see cref="IEnumSetupInstances"/> interface. If the method fails, this parameter is undefined.</param>
+    /// <returns>S_OK if a clone was returned; otherwise, E_OUTOFMEMORY.</returns>
+    STDMETHOD(Clone)(
+        _Deref_out_opt_ IEnumSetupInstances** ppenum
+        ) = 0;
+};
+#endif
+
+EXTERN_C const IID IID_ISetupConfiguration;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+/// <summary>
+/// Gets information about product instances installed on the machine.
+/// </summary>
+struct DECLSPEC_UUID("42843719-DB4C-46C2-8E7C-64F1816EFD5B") DECLSPEC_NOVTABLE ISetupConfiguration : public IUnknown
+{
+    /// <summary>
+    /// Enumerates all launchable product instances installed.
+    /// </summary>
+    /// <param name="ppEnumInstances">An enumeration of completed, installed product instances.</param>
+    /// <returns>Standard HRESULT indicating success or failure.</returns>
+    STDMETHOD(EnumInstances)(
+        _Out_ IEnumSetupInstances** ppEnumInstances
+        ) = 0;
+
+    /// <summary>
+    /// Gets the instance for the current process path.
+    /// </summary>
+    /// <param name="ppInstance">The instance for the current process path.</param>
+    /// <returns>
+    /// The instance for the current process path, or E_NOTFOUND if not found.
+    /// The <see cref="ISetupInstance::GetState"/> may indicate the instance is invalid.
+    /// </returns>
+    /// <remarks>
+    /// The returned instance may not be launchable.
+    /// </remarks>
+STDMETHOD(GetInstanceForCurrentProcess)(
+        _Out_ ISetupInstance** ppInstance
+        ) = 0;
+
+    /// <summary>
+    /// Gets the instance for the given path.
+    /// </summary>
+    /// <param name="ppInstance">The instance for the given path.</param>
+    /// <returns>
+    /// The instance for the given path, or E_NOTFOUND if not found.
+    /// The <see cref="ISetupInstance::GetState"/> may indicate the instance is invalid.
+    /// </returns>
+    /// <remarks>
+    /// The returned instance may not be launchable.
+    /// </remarks>
+STDMETHOD(GetInstanceForPath)(
+        _In_z_ LPCWSTR wzPath,
+        _Out_ ISetupInstance** ppInstance
+        ) = 0;
+};
+#endif
+
+EXTERN_C const IID IID_ISetupConfiguration2;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+/// <summary>
+/// Gets information about product instances.
+/// </summary>
+struct DECLSPEC_UUID("26AAB78C-4A60-49D6-AF3B-3C35BC93365D") DECLSPEC_NOVTABLE ISetupConfiguration2 : public ISetupConfiguration
+{
+    /// <summary>
+    /// Enumerates all product instances.
+    /// </summary>
+    /// <param name="ppEnumInstances">An enumeration of all product instances.</param>
+    /// <returns>Standard HRESULT indicating success or failure.</returns>
+    STDMETHOD(EnumAllInstances)(
+        _Out_ IEnumSetupInstances** ppEnumInstances
+        ) = 0;
+};
+#endif
+
+EXTERN_C const IID IID_ISetupPackageReference;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+/// <summary>
+/// A reference to a package.
+/// </summary>
+struct DECLSPEC_UUID("da8d8a16-b2b6-4487-a2f1-594ccccd6bf5") DECLSPEC_NOVTABLE ISetupPackageReference : public IUnknown
+{
+    /// <summary>
+    /// Gets the general package identifier.
+    /// </summary>
+    /// <param name="pbstrId">The general package identifier.</param>
+    /// <returns>Standard HRESULT indicating success or failure.</returns>
+    STDMETHOD(GetId)(
+        _Out_ BSTR* pbstrId
+        ) = 0;
+
+    /// <summary>
+    /// Gets the version of the package.
+    /// </summary>
+    /// <param name="pbstrVersion">The version of the package.</param>
+    /// <returns>Standard HRESULT indicating success or failure.</returns>
+    STDMETHOD(GetVersion)(
+        _Out_ BSTR* pbstrVersion
+        ) = 0;
+
+    /// <summary>
+    /// Gets the target process architecture of the package.
+    /// </summary>
+    /// <param name="pbstrChip">The target process architecture of the package.</param>
+    /// <returns>Standard HRESULT indicating success or failure.</returns>
+    STDMETHOD(GetChip)(
+        _Out_ BSTR* pbstrChip
+        ) = 0;
+
+    /// <summary>
+    /// Gets the language and optional region identifier.
+    /// </summary>
+    /// <param name="pbstrLanguage">The language and optional region identifier.</param>
+    /// <returns>Standard HRESULT indicating success or failure.</returns>
+    STDMETHOD(GetLanguage)(
+        _Out_ BSTR* pbstrLanguage
+        ) = 0;
+
+    /// <summary>
+    /// Gets the build branch of the package.
+    /// </summary>
+    /// <param name="pbstrBranch">The build branch of the package.</param>
+    /// <returns>Standard HRESULT indicating success or failure.</returns>
+    STDMETHOD(GetBranch)(
+        _Out_ BSTR* pbstrBranch
+        ) = 0;
+
+    /// <summary>
+    /// Gets the type of the package.
+    /// </summary>
+    /// <param name="pbstrType">The type of the package.</param>
+    /// <returns>Standard HRESULT indicating success or failure.</returns>
+    STDMETHOD(GetType)(
+        _Out_ BSTR* pbstrType
+        ) = 0;
+
+    /// <summary>
+    /// Gets the unique identifier consisting of all defined tokens.
+    /// </summary>
+    /// <param name="pbstrUniqueId">The unique identifier consisting of all defined tokens.</param>
+    /// <returns>Standard HRESULT indicating success or failure, including E_UNEXPECTED if no Id was defined (required).</returns>
+    STDMETHOD(GetUniqueId)(
+        _Out_ BSTR* pbstrUniqueId
+        ) = 0;
+
+    /// <summary>
+    /// Gets a value indicating whether the package refers to an external extension.
+    /// </summary>
+    /// <param name="pfIsExtension">A value indicating whether the package refers to an external extension.</param>
+    /// <returns>Standard HRESULT indicating success or failure, including E_UNEXPECTED if no Id was defined (required).</returns>
+    STDMETHOD(GetIsExtension)(
+        _Out_ VARIANT_BOOL* pfIsExtension
+        ) = 0;
+};
+#endif
+
+EXTERN_C const IID IID_ISetupHelper;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+/// <summary>
+/// Helper functions.
+/// </summary>
+/// <remarks>
+/// You can query for this interface from the <see cref="SetupConfiguration"/> class.
+/// </remarks>
+struct DECLSPEC_UUID("42b21b78-6192-463e-87bf-d577838f1d5c") DECLSPEC_NOVTABLE ISetupHelper : public IUnknown
+{
+    /// <summary>
+    /// Parses a dotted quad version string into a 64-bit unsigned integer.
+    /// </summary>
+    /// <param name="pwszVersion">The dotted quad version string to parse, e.g. 1.2.3.4.</param>
+    /// <param name="pullVersion">A 64-bit unsigned integer representing the version. You can compare this to other versions.</param>
+    /// <returns>Standard HRESULT indicating success or failure, including E_INVALIDARG if the version is not valid.</returns>
+    STDMETHOD(ParseVersion)(
+        _In_ LPCOLESTR pwszVersion,
+        _Out_ PULONGLONG pullVersion
+        ) = 0;
+
+    /// <summary>
+    /// Parses a dotted quad version string into a 64-bit unsigned integer.
+    /// </summary>
+    /// <param name="pwszVersionRange">The string containing 1 or 2 dotted quad version strings to parse, e.g. [1.0,) that means 1.0.0.0 or newer.</param>
+    /// <param name="pullMinVersion">A 64-bit unsigned integer representing the minimum version, which may be 0. You can compare this to other versions.</param>
+    /// <param name="pullMaxVersion">A 64-bit unsigned integer representing the maximum version, which may be MAXULONGLONG. You can compare this to other versions.</param>
+    /// <returns>Standard HRESULT indicating success or failure, including E_INVALIDARG if the version range is not valid.</returns>
+    STDMETHOD(ParseVersionRange)(
+        _In_ LPCOLESTR pwszVersionRange,
+        _Out_ PULONGLONG pullMinVersion,
+        _Out_ PULONGLONG pullMaxVersion
+        ) = 0;
+};
+#endif
+
+EXTERN_C const IID IID_ISetupErrorState;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+/// <summary>
+/// Information about the error state of an instance.
+/// </summary>
+struct DECLSPEC_UUID("46DCCD94-A287-476A-851E-DFBC2FFDBC20") DECLSPEC_NOVTABLE ISetupErrorState : public IUnknown
+{
+    /// <summary>
+    /// Gets an array of failed package references.
+    /// </summary>
+    /// <param name="ppsaPackages">Pointer to an array of <see cref="ISetupFailedPackageReference"/>, if packages have failed.</param>
+    /// <returns>Standard HRESULT indicating success or failure.</returns>
+    STDMETHOD(GetFailedPackages)(
+        _Outptr_result_maybenull_ LPSAFEARRAY* ppsaFailedPackages
+        ) = 0;
+
+    /// <summary>
+    /// Gets an array of skipped package references.
+    /// </summary>
+    /// <param name="ppsaPackages">Pointer to an array of <see cref="ISetupPackageReference"/>, if packages have been skipped.</param>
+    /// <returns>Standard HRESULT indicating success or failure.</returns>
+    STDMETHOD(GetSkippedPackages)(
+        _Outptr_result_maybenull_ LPSAFEARRAY* ppsaSkippedPackages
+        ) = 0;
+};
+#endif
+
+EXTERN_C const IID IID_ISetupErrorState2;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+/// <summary>
+/// Information about the error state of an instance.
+/// </summary>
+struct DECLSPEC_UUID("9871385B-CA69-48F2-BC1F-7A37CBF0B1EF") DECLSPEC_NOVTABLE ISetupErrorState2 : public ISetupErrorState
+{
+    /// <summary>
+    /// Gets the path to the error log.
+    /// </summary>
+    /// <param name="pbstrChip">The path to the error log.</param>
+    /// <returns>Standard HRESULT indicating success or failure.</returns>
+    STDMETHOD(GetErrorLogFilePath)(
+        _Outptr_result_maybenull_ BSTR* pbstrErrorLogFilePath
+        ) = 0;
+};
+#endif
+
+EXTERN_C const IID IID_ISetupFailedPackageReference;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+/// <summary>
+/// A reference to a failed package.
+/// </summary>
+struct DECLSPEC_UUID("E73559CD-7003-4022-B134-27DC650B280F") DECLSPEC_NOVTABLE ISetupFailedPackageReference : public ISetupPackageReference
+{
+};
+
+#endif
+
+EXTERN_C const IID IID_ISetupFailedPackageReference2;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+/// <summary>
+/// A reference to a failed package.
+/// </summary>
+struct DECLSPEC_UUID("0FAD873E-E874-42E3-B268-4FE2F096B9CA") DECLSPEC_NOVTABLE ISetupFailedPackageReference2 : public ISetupFailedPackageReference
+{
+    /// <summary>
+    /// Gets the path to the optional package log.
+    /// </summary>
+    /// <param name="pbstrId">The path to the optional package log.</param>
+    /// <returns>Standard HRESULT indicating success or failure.</returns>
+    STDMETHOD(GetLogFilePath)(
+        _Outptr_result_maybenull_ BSTR* pbstrLogFilePath
+        ) = 0;
+
+    /// <summary>
+    /// Gets the description of the package failure.
+    /// </summary>
+    /// <param name="pbstrId">The description of the package failure.</param>
+    /// <returns>Standard HRESULT indicating success or failure.</returns>
+    STDMETHOD(GetDescription)(
+        _Outptr_result_maybenull_ BSTR* pbstrDescription
+        ) = 0;
+
+    /// <summary>
+    /// Gets the signature to use for feedback reporting.
+    /// </summary>
+    /// <param name="pbstrId">The signature to use for feedback reporting.</param>
+    /// <returns>Standard HRESULT indicating success or failure.</returns>
+    STDMETHOD(GetSignature)(
+        _Outptr_result_maybenull_ BSTR* pbstrSignature
+        ) = 0;
+
+    /// <summary>
+    ///  Gets the array of details for this package failure.
+    /// </summary>
+    /// <param name="ppsaDetails">Pointer to an array of details as BSTRs.</param>
+    /// <returns>Standard HRESULT indicating success or failure.</returns>
+    STDMETHOD(GetDetails)(
+        _Out_ LPSAFEARRAY* ppsaDetails
+        ) = 0;
+
+    /// <summary>
+    /// Gets an array of packages affected by this package failure.
+    /// </summary>
+    /// <param name="ppsaPackages">Pointer to an array of <see cref="ISetupPackageReference"/> for packages affected by this package failure. This may be NULL.</param>
+    /// <returns>Standard HRESULT indicating success or failure.</returns>
+    STDMETHOD(GetAffectedPackages)(
+        _Out_ LPSAFEARRAY* ppsaAffectedPackages
+        ) = 0;
+};
+
+#endif
+
+EXTERN_C const IID IID_ISetupPropertyStore;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+/// <summary>
+/// Provides named properties.
+/// </summary>
+/// <remarks>
+/// You can get this from an <see cref="ISetupInstance"/>, <see cref="ISetupPackageReference"/>, or derivative.
+/// </remarks>
+struct DECLSPEC_UUID("C601C175-A3BE-44BC-91F6-4568D230FC83") DECLSPEC_NOVTABLE ISetupPropertyStore : public IUnknown
+{
+    /// <summary>
+    /// Gets an array of property names in this property store.
+    /// </summary>
+    /// <param name="ppsaNames">Pointer to an array of property names as BSTRs.</param>
+    /// <returns>Standard HRESULT indicating success or failure.</returns>
+    STDMETHOD(GetNames)(
+        _Out_ LPSAFEARRAY* ppsaNames
+        ) = 0;
+
+    /// <summary>
+    /// Gets the value of a named property in this property store.
+    /// </summary>
+    /// <param name="pwszName">The name of the property to get.</param>
+    /// <param name="pvtValue">The value of the property.</param>
+    /// <returns>Standard HRESULT indicating success or failure, including E_NOTFOUND if the property is not defined or E_NOTSUPPORTED if the property type is not supported.</returns>
+    STDMETHOD(GetValue)(
+        _In_ LPCOLESTR pwszName,
+        _Out_ LPVARIANT pvtValue
+        ) = 0;
+};
+
+#endif
+
+EXTERN_C const IID IID_ISetupLocalizedPropertyStore;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+/// <summary>
+/// Provides localized named properties.
+/// </summary>
+/// <remarks>
+/// You can get this from an <see cref="ISetupLocalizedProperties"/>.
+/// </remarks>
+struct DECLSPEC_UUID("5BB53126-E0D5-43DF-80F1-6B161E5C6F6C") DECLSPEC_NOVTABLE ISetupLocalizedPropertyStore : public IUnknown
+{
+    /// <summary>
+    /// Gets an array of property names in this property store.
+    /// </summary>
+    /// <param name="lcid">The LCID for the property names.</param>
+    /// <param name="ppsaNames">Pointer to an array of property names as BSTRs.</param>
+    /// <returns>Standard HRESULT indicating success or failure.</returns>
+    STDMETHOD(GetNames)(
+        _In_ LCID lcid,
+        _Out_ LPSAFEARRAY* ppsaNames
+        ) = 0;
+
+    /// <summary>
+    /// Gets the value of a named property in this property store.
+    /// </summary>
+    /// <param name="pwszName">The name of the property to get.</param>
+    /// <param name="lcid">The LCID for the property.</param>
+    /// <param name="pvtValue">The value of the property.</param>
+    /// <returns>Standard HRESULT indicating success or failure, including E_NOTFOUND if the property is not defined or E_NOTSUPPORTED if the property type is not supported.</returns>
+    STDMETHOD(GetValue)(
+        _In_ LPCOLESTR pwszName,
+        _In_ LCID lcid,
+        _Out_ LPVARIANT pvtValue
+        ) = 0;
+};
+
+#endif
+
+// Class declarations
+//
+EXTERN_C const CLSID CLSID_SetupConfiguration;
+
+#ifdef __cplusplus
+/// <summary>
+/// This class implements <see cref="ISetupConfiguration"/>, <see cref="ISetupConfiguration2"/>, and <see cref="ISetupHelper"/>.
+/// </summary>
+class DECLSPEC_UUID("177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D") SetupConfiguration;
+#endif
+
+// Function declarations
+//
+/// <summary>
+/// Gets an <see cref="ISetupConfiguration"/> that provides information about product instances installed on the machine.
+/// </summary>
+/// <param name="ppConfiguration">The <see cref="ISetupConfiguration"/> that provides information about product instances installed on the machine.</param>
+/// <param name="pReserved">Reserved for future use.</param>
+/// <returns>Standard HRESULT indicating success or failure.</returns>
+STDMETHODIMP GetSetupConfiguration(
+    _Out_ ISetupConfiguration** ppConfiguration,
+    _Reserved_ LPVOID pReserved
+);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/PC/external/v140/amd64/Microsoft.VisualStudio.Setup.Configuration.Native.lib b/PC/external/v140/amd64/Microsoft.VisualStudio.Setup.Configuration.Native.lib
new file mode 100644 (file)
index 0000000..675a501
Binary files /dev/null and b/PC/external/v140/amd64/Microsoft.VisualStudio.Setup.Configuration.Native.lib differ
diff --git a/PC/external/v140/win32/Microsoft.VisualStudio.Setup.Configuration.Native.lib b/PC/external/v140/win32/Microsoft.VisualStudio.Setup.Configuration.Native.lib
new file mode 100644 (file)
index 0000000..40f70b2
Binary files /dev/null and b/PC/external/v140/win32/Microsoft.VisualStudio.Setup.Configuration.Native.lib differ
diff --git a/PC/external/v141/amd64/Microsoft.VisualStudio.Setup.Configuration.Native.lib b/PC/external/v141/amd64/Microsoft.VisualStudio.Setup.Configuration.Native.lib
new file mode 100644 (file)
index 0000000..675a501
Binary files /dev/null and b/PC/external/v141/amd64/Microsoft.VisualStudio.Setup.Configuration.Native.lib differ
diff --git a/PC/external/v141/win32/Microsoft.VisualStudio.Setup.Configuration.Native.lib b/PC/external/v141/win32/Microsoft.VisualStudio.Setup.Configuration.Native.lib
new file mode 100644 (file)
index 0000000..40f70b2
Binary files /dev/null and b/PC/external/v141/win32/Microsoft.VisualStudio.Setup.Configuration.Native.lib differ
index 4e73d60..8e1bc02 100644 (file)
@@ -1,20 +1,20 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
-  <trustInfo>
+  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
     <security>
       <requestedPrivileges>
         <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
       </requestedPrivileges>
     </security>
   </trustInfo>
-  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> 
-    <application> 
-      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> 
+  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+    <application>
+      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
       <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
       <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
       <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
       <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
-    </application> 
+    </application>
   </compatibility>
   <application xmlns="urn:schemas-microsoft-com:asm.v3">
     <windowsSettings>
index 7feebcb..0d3f045 100644 (file)
@@ -101,7 +101,7 @@ winsound_PlaySound_impl(PyObject *module, PyObject *sound, int flags)
                          Py_TYPE(sound)->tp_name);
             return NULL;
         }
-        wsound = PyUnicode_AsWideCharString(sound, NULL);
+        wsound = _PyUnicode_AsWideCharString(sound);
         if (wsound == NULL) {
             return NULL;
         }
index e7cdf57..eaa9a06 100644 (file)
@@ -87,6 +87,7 @@
   </ItemGroup>\r
   <ItemGroup>\r
     <ClCompile Include="..\Modules\_elementtree.c" />\r
+    <ClCompile Include="..\Modules\expat\loadlibrary.c" />\r
     <ClCompile Include="..\Modules\expat\xmlparse.c" />\r
     <ClCompile Include="..\Modules\expat\xmlrole.c" />\r
     <ClCompile Include="..\Modules\expat\xmltok.c" />\r
index acb722d..14fa417 100644 (file)
@@ -33,6 +33,9 @@
     <ClInclude Include="..\Modules\expat\latin1tab.h">\r
       <Filter>Header Files</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="..\Modules\expat\loadlibrary.c">\r
+      <Filter>Header Files</Filter>\r
+    </ClInclude>\r
     <ClInclude Include="..\Modules\expat\macconfig.h">\r
       <Filter>Header Files</Filter>\r
     </ClInclude>\r
@@ -69,4 +72,4 @@
       <Filter>Source Files</Filter>\r
     </ClCompile>\r
   </ItemGroup>\r
-</Project>
\ No newline at end of file
+</Project>\r
index c684921..3aac1d7 100644 (file)
@@ -65,7 +65,7 @@
       <PreprocessorDefinitions>WIN32;_FILE_OFFSET_BITS=64;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;LZMA_API_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
     </ClCompile>\r
     <Link>\r
-      <AdditionalDependencies>$(OutDir)/liblzma$(PyDebugExt).lib</AdditionalDependencies>\r
+      <AdditionalDependencies>$(OutDir)liblzma$(PyDebugExt).lib;%(AdditionalDependencies)</AdditionalDependencies>\r
     </Link>\r
   </ItemDefinitionGroup>\r
   <ItemGroup>\r
index 41be720..18a9931 100644 (file)
@@ -5,8 +5,6 @@ echo.%~nx0 [flags and arguments] [quoted MSBuild options]
 echo.\r
 echo.Build CPython from the command line.  Requires the appropriate\r
 echo.version(s) of Microsoft Visual Studio to be installed (see readme.txt).\r
-echo.Also requires Subversion (svn.exe) to be on PATH if the '-e' flag is\r
-echo.given.\r
 echo.\r
 echo.After the flags recognized by this script, up to 9 arguments to be passed\r
 echo.directly to MSBuild may be passed.  If the argument contains an '=', the\r
@@ -79,7 +77,7 @@ if "%~1"=="-e" (set IncludeExternals=true) & shift & goto CheckOpts
 if "%~1"=="--no-ssl" (set IncludeSSL=false) & shift & goto CheckOpts\r
 if "%~1"=="--no-tkinter" (set IncludeTkinter=false) & shift & goto CheckOpts\r
 \r
-if "%IncludeExternals%"=="" set IncludeExternals=false\r
+if "%IncludeExternals%"=="" set IncludeExternals=true\r
 if "%IncludeSSL%"=="" set IncludeSSL=true\r
 if "%IncludeTkinter%"=="" set IncludeTkinter=true\r
 \r
@@ -106,7 +104,7 @@ if "%kill%"=="true" call :Kill
 \r
 if "%do_pgo%"=="true" (\r
     set conf=PGInstrument\r
-    call :Build\r
+    call :Build %1 %2 %3 %4 %5 %6 %7 %8 %9\r
     del /s "%dir%\*.pgc"\r
     del /s "%dir%\..\Lib\*.pyc"\r
     echo on\r
index 1a8bfa8..4676395 100644 (file)
 @where msbuild > "%TEMP%\msbuild.loc" 2> nul && set /P MSBUILD= < "%TEMP%\msbuild.loc" & del "%TEMP%\msbuild.loc"\r
 @if exist "%MSBUILD%" set MSBUILD="%MSBUILD%" & (set _Py_MSBuild_Source=PATH) & goto :found\r
 \r
-@rem VS 2017 sets exactly one install as the "main" install, so we may find MSBuild in there.\r
-@reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\SxS\VS7" /v 15.0 /reg:32 >nul 2>nul\r
-@if NOT ERRORLEVEL 1 @for /F "tokens=1,2*" %%i in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\SxS\VS7" /v 15.0 /reg:32') DO @(\r
-    @if "%%i"=="15.0" @if exist "%%k\MSBuild\15.0\Bin\msbuild.exe" @(set MSBUILD="%%k\MSBuild\15.0\Bin\msbuild.exe")\r
-)\r
-@if exist %MSBUILD% (set _Py_MSBuild_Source=Visual Studio 2017 registry) & goto :found\r
-\r
 @rem VS 2015 and earlier register MSBuild separately, so we can find it.\r
+@rem Prefer MSBuild 14.0 over MSBuild 15.0, since the latter may not be able to find a VC14 install.\r
 @reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0" /v MSBuildToolsPath /reg:32 >nul 2>nul\r
 @if NOT ERRORLEVEL 1 @for /F "tokens=1,2*" %%i in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0" /v MSBuildToolsPath /reg:32') DO @(\r
     @if "%%i"=="MSBuildToolsPath" @if exist "%%k\msbuild.exe" @(set MSBUILD="%%k\msbuild.exe")\r
 )\r
 @if exist %MSBUILD% (set _Py_MSBuild_Source=registry) & goto :found\r
 \r
+@rem VS 2017 sets exactly one install as the "main" install, so we may find MSBuild in there.\r
+@reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\SxS\VS7" /v 15.0 /reg:32 >nul 2>nul\r
+@if NOT ERRORLEVEL 1 @for /F "tokens=1,2*" %%i in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\SxS\VS7" /v 15.0 /reg:32') DO @(\r
+    @if "%%i"=="15.0" @if exist "%%k\MSBuild\15.0\Bin\msbuild.exe" @(set MSBUILD="%%k\MSBuild\15.0\Bin\msbuild.exe")\r
+)\r
+@if exist %MSBUILD% (set _Py_MSBuild_Source=Visual Studio 2017 registry) & goto :found\r
+\r
 \r
 @exit /b 1\r
 \r
diff --git a/PCbuild/find_python.bat b/PCbuild/find_python.bat
new file mode 100644 (file)
index 0000000..6e81200
--- /dev/null
@@ -0,0 +1,75 @@
+@rem\r
+@rem Searches for python.exe and may download a private copy from nuget.\r
+@rem\r
+@rem This file is supposed to modify the state of the caller (specifically\r
+@rem the MSBUILD variable), so we do not use setlocal or echo, and avoid\r
+@rem changing any other persistent state.\r
+@rem\r
+\r
+@rem No arguments provided means do full search\r
+@if '%1' EQU '' goto :begin_search\r
+\r
+@rem One argument may be the full path. Use a goto so we don't try to\r
+@rem parse the next if statement - incorrect quoting in the multi-arg\r
+@rem case can cause us to break immediately.\r
+@if '%2' EQU '' goto :one_arg\r
+\r
+@rem Entire command line may represent the full path if quoting failed.\r
+@if exist "%*" (set PYTHON="%*") & (set _Py_Python_Source=from environment) & goto :found\r
+@goto :begin_search\r
+\r
+:one_arg\r
+@if exist "%~1" (set PYTHON="%~1") & (set _Py_Python_Source=from environment) & goto :found\r
+\r
+:begin_search\r
+@set PYTHON=\r
+\r
+@set _Py_EXTERNALS_DIR=%EXTERNAL_DIR%\r
+@if "%_Py_EXTERNALS_DIR%"=="" (set _Py_EXTERNALS_DIR=%~dp0\..\externals)\r
+\r
+@rem If we have Python in externals, use that one\r
+@if exist "%_Py_EXTERNALS_DIR%\pythonx86\tools\python.exe" (set PYTHON="%_Py_EXTERNALS_DIR%\pythonx86\tools\python.exe") & (set _Py_Python_Source=found in externals directory) & goto :found\r
+\r
+@rem If HOST_PYTHON is recent enough, use that\r
+@if NOT "%HOST_PYTHON%"=="" @%HOST_PYTHON% -c "import sys; assert sys.version_info[:2] >= (3, 6)" >nul 2>nul && (set PYTHON="%HOST_PYTHON%") && (set _Py_Python_Source=found as HOST_PYTHON) && goto :found\r
+\r
+@rem If py.exe finds a recent enough version, use that one\r
+@py -3.6 -V >nul 2>&1 && (set PYTHON=py -3.6) && (set _Py_Python_Source=found with py.exe) && goto :found\r
+\r
+@if NOT exist "%_Py_EXTERNALS_DIR%" mkdir "%_Py_EXTERNALS_DIR%"\r
+@set _Py_NUGET=%NUGET%\r
+@set _Py_NUGET_URL=%NUGET_URL%\r
+@set _Py_HOST_PYTHON=%HOST_PYTHON%\r
+@if "%_Py_HOST_PYTHON%"=="" set _Py_HOST_PYTHON=py\r
+@if "%_Py_NUGET%"=="" (set _Py_NUGET=%_Py_EXTERNALS_DIR%\nuget.exe)\r
+@if "%_Py_NUGET_URL%"=="" (set _Py_NUGET_URL=https://aka.ms/nugetclidl)\r
+@if NOT exist "%_Py_NUGET%" (\r
+    @echo Downloading nuget...\r
+    @rem NB: Must use single quotes around NUGET here, NOT double!\r
+    @rem Otherwise, a space in the path would break things\r
+    @rem If it fails, retry with any available copy of Python\r
+    @powershell.exe -Command Invoke-WebRequest %_Py_NUGET_URL% -OutFile '%_Py_NUGET%'\r
+    @if errorlevel 1 (\r
+        @%_Py_HOST_PYTHON% "%~dp0\urlretrieve.py" "%_Py_NUGET_URL%" "%_Py_NUGET%"\r
+    )\r
+)\r
+@echo Installing Python via nuget...\r
+@"%_Py_NUGET%" install pythonx86 -ExcludeVersion -OutputDirectory "%_Py_EXTERNALS_DIR%"\r
+@rem Quote it here; it's not quoted later because "py -3.6" wouldn't work\r
+@if not errorlevel 1 (set PYTHON="%_Py_EXTERNALS_DIR%\pythonx86\tools\python.exe") & (set _Py_Python_Source=found on nuget.org) & goto :found\r
+\r
+\r
+@set _Py_Python_Source=\r
+@set _Py_EXTERNALS_DIR=\r
+@set _Py_NUGET=\r
+@set _Py_NUGET_URL=\r
+@set _Py_HOST_PYTHON=\r
+@exit /b 1\r
+\r
+:found\r
+@echo Using %PYTHON% (%_Py_Python_Source%)\r
+@set _Py_Python_Source=\r
+@set _Py_EXTERNALS_DIR=\r
+@set _Py_NUGET=\r
+@set _Py_NUGET_URL=\r
+@set _Py_HOST_PYTHON=\r
index a04a81e..8fde64d 100644 (file)
@@ -4,8 +4,6 @@ rem Simple script to fetch source for external libraries
 \r
 if "%PCBUILD%"=="" (set PCBUILD=%~dp0)\r
 if "%EXTERNALS_DIR%"=="" (set EXTERNALS_DIR=%PCBUILD%\..\externals)\r
-if "%NUGET%"=="" (set NUGET=%EXTERNALS_DIR%\nuget.exe)\r
-if "%NUGET_URL%"=="" (set NUGET_URL=https://aka.ms/nugetclidl)\r
 \r
 set DO_FETCH=true\r
 set DO_CLEAN=false\r
@@ -13,7 +11,7 @@ set DO_CLEAN=false
 :CheckOpts\r
 if "%~1"=="--no-tkinter" (set IncludeTkinter=false) & shift & goto CheckOpts\r
 if "%~1"=="--no-openssl" (set IncludeSSL=false) & shift & goto CheckOpts\r
-if "%~1"=="--python" (set PYTHON_FOR_BUILD=%2) & shift & shift & goto CheckOpts\r
+if "%~1"=="--python" (set PYTHON=%2) & shift & shift & goto CheckOpts\r
 if "%~1"=="--organization" (set ORG=%2) & shift & shift & goto CheckOpts\r
 if "%~1"=="-c" (set DO_CLEAN=true) & shift & goto CheckOpts\r
 if "%~1"=="--clean" (set DO_CLEAN=true) & shift & goto CheckOpts\r
@@ -33,22 +31,10 @@ if "%DO_FETCH%"=="false" goto end
 \r
 if "%ORG%"=="" (set ORG=python)\r
 \r
-if "%PYTHON_FOR_BUILD%"=="" (\r
-    echo Checking for installed python...\r
-    py -3.6 -V >nul 2>&1 && (set PYTHON_FOR_BUILD=py -3.6)\r
-)\r
-if "%PYTHON_FOR_BUILD%"=="" (\r
-    if NOT exist "%EXTERNALS_DIR%" mkdir "%EXTERNALS_DIR%"\r
-    if NOT exist "%NUGET%" (\r
-        echo Downloading nuget...\r
-        rem NB: Must use single quotes around NUGET here, NOT double!\r
-        rem Otherwise, a space in the path would break things\r
-        powershell.exe -Command Invoke-WebRequest %NUGET_URL% -OutFile '%NUGET%'\r
-    )\r
-    echo Installing Python via nuget...\r
-    "%NUGET%" install pythonx86 -ExcludeVersion -OutputDirectory "%EXTERNALS_DIR%"\r
-    rem Quote it here; it's not quoted later because "py -3.6" wouldn't work\r
-    set PYTHON_FOR_BUILD="%EXTERNALS_DIR%\pythonx86\tools\python.exe"\r
+call "%PCBUILD%find_python.bat" "%PYTHON%"\r
+\r
+if "%PYTHON%"=="" (\r
+    where /Q git || echo Python 3.6 could not be found or installed, and git.exe is not on your PATH && exit /B 1\r
 )\r
 \r
 echo.Fetching external libraries...\r
@@ -65,9 +51,12 @@ set libraries=%libraries%                                    xz-5.2.2
 for %%e in (%libraries%) do (\r
     if exist "%EXTERNALS_DIR%\%%e" (\r
         echo.%%e already exists, skipping.\r
+    ) else if "%PYTHON%"=="" (\r
+        echo.Fetching %%e with git...\r
+        git clone --depth 1 https://github.com/%ORG%/cpython-source-deps --branch %%e "%EXTERNALS_DIR%\%%e"\r
     ) else (\r
         echo.Fetching %%e...\r
-        %PYTHON_FOR_BUILD% "%PCBUILD%get_external.py" -O %ORG% %%e\r
+        %PYTHON% "%PCBUILD%get_external.py" -O %ORG% %%e\r
     )\r
 )\r
 \r
@@ -80,9 +69,12 @@ if NOT "%IncludeSSL%"=="false" set binaries=%binaries%     nasm-2.11.06
 for %%b in (%binaries%) do (\r
     if exist "%EXTERNALS_DIR%\%%b" (\r
         echo.%%b already exists, skipping.\r
+    ) else if "%PYTHON%"=="" (\r
+        echo.Fetching %%b with git...\r
+        git clone --depth 1 https://github.com/%ORG%/cpython-bin-deps --branch %%b "%EXTERNALS_DIR%\%%b"\r
     ) else (\r
         echo.Fetching %%b...\r
-        %PYTHON_FOR_BUILD% "%PCBUILD%get_external.py" -b -O %ORG% %%b\r
+        %PYTHON% "%PCBUILD%get_external.py" -b -O %ORG% %%b\r
     )\r
 )\r
 \r
index 5e28748..cd3f6d1 100644 (file)
@@ -68,6 +68,7 @@
   </ItemGroup>\r
   <ItemGroup>\r
     <ClCompile Include="..\Modules\pyexpat.c" />\r
+    <ClCompile Include="..\Modules\expat\loadlibrary.c" />\r
     <ClCompile Include="..\Modules\expat\xmlparse.c" />\r
     <ClCompile Include="..\Modules\expat\xmlrole.c" />\r
     <ClCompile Include="..\Modules\expat\xmltok.c" />\r
@@ -84,4 +85,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r
   <ImportGroup Label="ExtensionTargets">\r
   </ImportGroup>\r
-</Project>
\ No newline at end of file
+</Project>\r
index bfa04d5..2d41521 100644 (file)
@@ -20,6 +20,9 @@
     <ClCompile Include="..\Modules\pyexpat.c">\r
       <Filter>Source Files</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="..\Modules\expat\loadlibrary.c">\r
+      <Filter>Source Files</Filter>\r
+    </ClCompile>\r
     <ClCompile Include="..\Modules\expat\xmlparse.c">\r
       <Filter>Source Files</Filter>\r
     </ClCompile>\r
@@ -30,4 +33,4 @@
       <Filter>Source Files</Filter>\r
     </ClCompile>\r
   </ItemGroup>\r
-</Project>
\ No newline at end of file
+</Project>\r
index fd25809..fdd8202 100644 (file)
@@ -147,13 +147,28 @@ foreach (System.Diagnostics.Process p in System.Diagnostics.Process.GetProcesses
              Targets="CleanAll" />\r
   </Target>\r
 \r
+  <Target Name="CopyPGCFiles" BeforeTargets="PrepareForBuild" Condition="$(Configuration) == 'PGUpdate'">\r
+    <ItemGroup>\r
+      <_PGCFiles Include="$(OutDir)instrumented\$(TargetName)!*.pgc" />\r
+      <_PGDFile Include="$(OutDir)instrumented\$(TargetName).pgd" />\r
+      <_CopyFiles Include="@(_PGCFiles);@(_PGDFile)" Condition="Exists(%(FullPath))" />\r
+    </ItemGroup>\r
+    <Delete Files="@(_CopyFiles->'$(OutDir)%(Filename)%(Extension)')" />\r
+    <Error Text="PGO run did not succeed (no $(TargetName)!*.pgc files) and there is no data to merge"\r
+           Condition="$(RequirePGCFiles) == 'true' and @(_PGCFiles) == ''" />\r
+    <Copy SourceFiles="@(_CopyFiles)"\r
+          DestinationFolder="$(OutDir)"\r
+          UseHardLinksIfPossible="true"\r
+          OverwriteReadOnlyFiles="true" />\r
+  </Target>\r
+\r
   <PropertyGroup>\r
-    <SdkBinPath Condition="'$(SdkBinPath)' == '' or !Exists($(SdkBinPath))">$(registry:HKEY_LOCAL_MACHINE\Software\Microsoft\Windows Kits\Installed Roots@KitsRoot10)\bin\x86</SdkBinPath>\r
+    <SdkBinPath Condition="'$(SdkBinPath)' == '' or !Exists($(SdkBinPath))">$(registry:HKEY_LOCAL_MACHINE\Software\Microsoft\Windows Kits\Installed Roots@KitsRoot10)\bin\$(DefaultWindowsSDKVersion)\x86</SdkBinPath>\r
+    <SdkBinPath Condition="!Exists($(SdkBinPath))">$(registry:HKEY_LOCAL_MACHINE\Software\Microsoft\Windows Kits\Installed Roots@KitsRoot10)\bin\x86</SdkBinPath>\r
     <SdkBinPath Condition="!Exists($(SdkBinPath))">$(registry:HKEY_LOCAL_MACHINE\Software\Microsoft\Windows Kits\Installed Roots@KitsRoot81)\bin\x86</SdkBinPath>\r
     <SdkBinPath Condition="!Exists($(SdkBinPath))">$(registry:HKEY_LOCAL_MACHINE\Software\Microsoft\Windows Kits\Installed Roots@KitsRoot)\bin\x86</SdkBinPath>\r
     <SdkBinPath Condition="!Exists($(SdkBinPath))">$(registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1A@InstallationFolder)\Bin\</SdkBinPath>\r
-    <_SignCommand Condition="Exists($(SdkBinPath)) and '$(SigningCertificate)' != '' and $(SupportSigning)">"$(SdkBinPath)\signtool.exe" sign /q /n "$(SigningCertificate)" /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d "Python $(PythonVersion)"</_SignCommand>\r
-    <_MakeCatCommand Condition="Exists($(SdkBinPath))">"$(SdkBinPath)\makecat.exe"</_MakeCatCommand>\r
+    <_SignCommand Condition="Exists($(SdkBinPath)) and '$(SigningCertificate)' != '' and $(SupportSigning)">"$(SdkBinPath)\signtool.exe" sign /q /a /n "$(SigningCertificate)" /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d "Python $(PythonVersion)"</_SignCommand>    <_MakeCatCommand Condition="Exists($(SdkBinPath))">"$(SdkBinPath)\makecat.exe"</_MakeCatCommand>\r
   </PropertyGroup>\r
   \r
   <Target Name="_SignBuild" AfterTargets="AfterBuild" Condition="'$(SigningCertificate)' != '' and $(SupportSigning)">\r
index a74135e..2beb596 100644 (file)
@@ -10,6 +10,7 @@
 \r
     We set BasePlatformToolset for ICC's benefit, it's otherwise ignored.\r
     -->\r
+    <BasePlatformToolset Condition="'$(BasePlatformToolset)' == '' and ('$(MSBuildToolsVersion)' == '15.0' or '$(VisualStudioVersion)' == '15.0')">v141</BasePlatformToolset>\r
     <BasePlatformToolset Condition="'$(BasePlatformToolset)' == '' and '$(VCTargetsPath14)' != ''">v140</BasePlatformToolset>\r
     <BasePlatformToolset Condition="'$(BasePlatformToolset)' == '' and '$(VCTargetsPath12)' != ''">v120</BasePlatformToolset>\r
     <BasePlatformToolset Condition="'$(BasePlatformToolset)' == '' and '$(VCTargetsPath11)' != ''">v110</BasePlatformToolset>\r
@@ -39,6 +40,7 @@
     <BuildPath Condition="'$(ArchName)' == 'amd64'">$(BuildPath64)</BuildPath>\r
     <BuildPath Condition="'$(BuildPath)' == ''">$(PySourcePath)PCBuild\$(ArchName)\</BuildPath>\r
     <BuildPath Condition="!HasTrailingSlash($(BuildPath))">$(BuildPath)\</BuildPath>\r
+    <BuildPath Condition="$(Configuration) == 'PGInstrument'">$(BuildPath)instrumented\</BuildPath>\r
     \r
     <!-- Directories of external projects. tcltk is handled in tcltk.props -->\r
     <ExternalsDir>$([System.IO.Path]::GetFullPath(`$(PySourcePath)externals\`))</ExternalsDir>\r
     <!-- Full path of the resulting python.exe binary -->\r
     <PythonExe Condition="'$(PythonExe)' == ''">$(BuildPath)python$(PyDebugExt).exe</PythonExe>\r
   </PropertyGroup>\r
-  \r
+\r
+  <PropertyGroup Condition="$(DefaultWindowsSDKVersion) == ''">\r
+    <!--\r
+    Attempt to select the latest installed WinSDK. If we don't find any, then we will\r
+    let the MSBuild targets determine which one it wants to use (typically the earliest\r
+    possible version). Since we limit WINVER to Windows 7 anyway, it doesn't really\r
+    matter which WinSDK version we use.\r
+    -->\r
+    <DefaultWindowsSDKVersion Condition="$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0@ProductVersion) == '10.0.15063'">10.0.15063.0</DefaultWindowsSDKVersion>\r
+    <DefaultWindowsSDKVersion Condition="$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Microsoft SDKs\Windows\v10.0@ProductVersion) == '10.0.15063'">10.0.15063.0</DefaultWindowsSDKVersion>\r
+    <DefaultWindowsSDKVersion Condition="$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0@ProductVersion) == '10.0.14393'">10.0.14393.0</DefaultWindowsSDKVersion>\r
+    <DefaultWindowsSDKVersion Condition="$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Microsoft SDKs\Windows\v10.0@ProductVersion) == '10.0.14393'">10.0.14393.0</DefaultWindowsSDKVersion>\r
+    <DefaultWindowsSDKVersion Condition="$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0@ProductVersion) == '10.0.10586'">10.0.10586.0</DefaultWindowsSDKVersion>\r
+    <DefaultWindowsSDKVersion Condition="$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Microsoft SDKs\Windows\v10.0@ProductVersion) == '10.0.10586'">10.0.10586.0</DefaultWindowsSDKVersion>\r
+    <DefaultWindowsSDKVersion Condition="$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0@ProductVersion) == '10.0.10240'">10.0.10240.0</DefaultWindowsSDKVersion>\r
+    <DefaultWindowsSDKVersion Condition="$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Microsoft SDKs\Windows\v10.0@ProductVersion) == '10.0.10240'">10.0.10240.0</DefaultWindowsSDKVersion>\r
+  </PropertyGroup>\r
+\r
   <PropertyGroup Condition="'$(OverrideVersion)' == ''">\r
     <!--\r
     Read version information from Include\patchlevel.h. The following properties are set:\r
index edbc3c5..a290e31 100644 (file)
@@ -96,6 +96,7 @@ set PYTHONPATH=$(PySourcePath)Lib
     <PropertyGroup>\r
       <_PGOPath Condition="$(Configuration) == 'PGInstrument' and $(Platform) == 'Win32'">@set PATH=%PATH%%3B$(VCInstallDir)bin</_PGOPath>\r
       <_PGOPath Condition="$(Configuration) == 'PGInstrument' and $(Platform) == 'x64'">@set PATH=%PATH%%3B$(VCInstallDir)bin\amd64</_PGOPath>\r
+      <_PGOPath Condition="$(Configuration) == 'PGInstrument' and $(VC_PGO_RunTime_Dir) != ''">@set PATH=%PATH%%3B$(VC_PGO_RunTime_Dir)</_PGOPath>\r
       <_Content>@rem This script invokes the most recently built Python with all arguments\r
 @rem passed through to the interpreter.  This file is generated by the\r
 @rem build process and any changes *will* be thrown away by the next\r
index d7e8386..d2355c9 100644 (file)
@@ -49,6 +49,7 @@
   </ImportGroup>\r
   <PropertyGroup>\r
     <KillPython>true</KillPython>\r
+    <RequirePGCFiles>true</RequirePGCFiles>\r
   </PropertyGroup>\r
   <ImportGroup Label="PropertySheets">\r
     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
@@ -70,6 +71,7 @@
     </ClCompile>\r
     <Link>\r
       <AdditionalDependencies>version.lib;shlwapi.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>\r
+      <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories);$(PySourcePath)PC\external\$(PlatformToolset)\$(ArchName)</AdditionalLibraryDirectories>\r
       <BaseAddress>0x1e000000</BaseAddress>\r
     </Link>\r
   </ItemDefinitionGroup>\r
     <ClInclude Include="..\Python\wordcode_helpers.h" />\r
   </ItemGroup>\r
   <ItemGroup>\r
+    <ClCompile Include="..\Modules\_asynciomodule.c" />\r
     <ClCompile Include="..\Modules\_bisectmodule.c" />\r
     <ClCompile Include="..\Modules\_blake2\blake2module.c" />\r
     <ClCompile Include="..\Modules\_blake2\blake2b_impl.c" />\r
     <ClCompile Include="..\PC\config.c" />\r
     <ClCompile Include="..\PC\getpathp.c" />\r
     <ClCompile Include="..\PC\msvcrtmodule.c" />\r
+    <ClCompile Include="..\PC\_findvs.cpp" />\r
     <ClCompile Include="..\Python\pyhash.c" />\r
     <ClCompile Include="..\Python\random.c" />\r
     <ClCompile Include="..\Python\_warnings.c" />\r
       </ClCompile>\r
     </ItemGroup>\r
   </Target>\r
-  <Target Name="_WarnAboutToolset" BeforeTargets="PrepareForBuild" Condition="$(PlatformToolset) != 'v140'">\r
+  <Target Name="_WarnAboutToolset" BeforeTargets="PrepareForBuild" Condition="$(PlatformToolset) != 'v140' and $(PlatformToolset) != 'v141'">\r
     <Warning Text="Toolset $(PlatformToolset) is not used for official builds. Your build may have errors or incompatibilities." />\r
   </Target>\r
 </Project>\r
index 69d98be..ac4e2c5 100644 (file)
@@ -63,7 +63,7 @@
   <PropertyGroup>\r
     <TclOpts>msvcrt</TclOpts>\r
     <TclOpts Condition="$(Configuration) == 'Debug'">symbols,msvcrt</TclOpts>\r
-    <TclDirs>INSTALLDIR="$(OutDir.TrimEnd(`\`))" INSTALL_DIR="$(OutDir.TrimEnd(`\`))"</TclDirs>\r
+    <TclDirs>BUILDDIRTOP="$(BuildDirTop)" INSTALLDIR="$(OutDir.TrimEnd(`\`))" INSTALL_DIR="$(OutDir.TrimEnd(`\`))"</TclDirs>\r
     <DebugFlags Condition="'$(Configuration)' == 'Debug'">DEBUGFLAGS="-wd4456 -wd4457 -wd4458 -wd4459 -wd4996"</DebugFlags>\r
     <NMakeBuildCommandLine>setlocal\r
 @(ExpectedOutputs->'if not exist "%(FullPath)" goto build','\r
index a67debd..8e6e127 100644 (file)
@@ -37,6 +37,7 @@
     <BuildDirTop>Release</BuildDirTop>\r
     <BuildDirTop Condition="$(Configuration) == 'Debug'">Debug</BuildDirTop>\r
     <BuildDirTop Condition="$(TclMachine) != 'IX86'">$(BuildDirTop)_$(TclMachine)</BuildDirTop>\r
+    <BuildDirTop Condition="$(PlatformToolset) == 'v141'">$(BuildDirTop)_VC13</BuildDirTop>\r
     <BuildDirTop Condition="$(PlatformToolset) == 'v140'">$(BuildDirTop)_VC13</BuildDirTop>\r
     <BuildDirTop Condition="$(PlatformToolset) == 'v120'">$(BuildDirTop)_VC12</BuildDirTop>\r
     <BuildDirTop Condition="$(PlatformToolset) == 'v110'">$(BuildDirTop)_VC11</BuildDirTop>\r
index 8d1aacb..7ab9305 100644 (file)
@@ -62,7 +62,7 @@
   <PropertyGroup>\r
     <TkOpts>msvcrt</TkOpts>\r
     <TkOpts Condition="$(Configuration) == 'Debug'">symbols,msvcrt</TkOpts>\r
-    <TkDirs>TCLDIR="$(tclDir.TrimEnd(`\`))" INSTALLDIR="$(OutDir.TrimEnd(`\`))"</TkDirs>\r
+    <TkDirs>BUILDDIRTOP="$(BuildDirTop)" TCLDIR="$(tclDir.TrimEnd(`\`))" INSTALLDIR="$(OutDir.TrimEnd(`\`))"</TkDirs>\r
     <DebugFlags Condition="'$(Configuration)' == 'Debug'">DEBUGFLAGS="-wd4456 -wd4457 -wd4458 -wd4459 -wd4996"</DebugFlags>\r
     <NMakeBuildCommandLine>setlocal\r
 @(ExpectedOutputs->'if not exist "%(FullPath)" goto build','\r
diff --git a/PCbuild/urlretrieve.py b/PCbuild/urlretrieve.py
new file mode 100644 (file)
index 0000000..9df773c
--- /dev/null
@@ -0,0 +1,39 @@
+# Simple Python script to download a file. Used as a fallback
+# when other more reliable methods fail.
+#
+from __future__ import print_function
+import sys
+
+try:
+    from requests import get
+except ImportError:
+    try:
+        from urllib.request import urlretrieve
+        USING = "urllib.request.urlretrieve"
+    except ImportError:
+        try:
+            from urllib import urlretrieve
+            USING = "urllib.retrieve"
+        except ImportError:
+            print("Python at", sys.executable, "is not suitable",
+                  "for downloading files.", file=sys.stderr)
+            sys.exit(2)
+else:
+    USING = "requests.get"
+
+    def urlretrieve(url, filename):
+        r = get(url, stream=True)
+        r.raise_for_status()
+        with open(filename, 'wb') as f:
+            for chunk in r.iter_content(chunk_size=1024):
+                f.write(chunk)
+        return filename
+
+if __name__ == '__main__':
+    if len(sys.argv) != 3:
+        print("Usage: urlretrieve.py [url] [filename]", file=sys.stderr)
+        sys.exit(1)
+    URL = sys.argv[1]
+    FILENAME = sys.argv[2]
+    print("Downloading from", URL, "to", FILENAME, "using", USING)
+    urlretrieve(URL, FILENAME)
index 725d73b..1e5f4d9 100644 (file)
@@ -522,8 +522,8 @@ class Obj2ModVisitor(PickleVisitor):
                 self.emit("%s = _Py_asdl_seq_new(len, arena);" % field.name, depth+1)
             self.emit("if (%s == NULL) goto failed;" % field.name, depth+1)
             self.emit("for (i = 0; i < len; i++) {", depth+1)
-            self.emit("%s value;" % ctype, depth+2)
-            self.emit("res = obj2ast_%s(PyList_GET_ITEM(tmp, i), &value, arena);" %
+            self.emit("%s val;" % ctype, depth+2)
+            self.emit("res = obj2ast_%s(PyList_GET_ITEM(tmp, i), &val, arena);" %
                       field.type, depth+2, reflow=False)
             self.emit("if (res != 0) goto failed;", depth+2)
             self.emit("if (len != PyList_GET_SIZE(tmp)) {", depth+2)
@@ -533,7 +533,7 @@ class Obj2ModVisitor(PickleVisitor):
                       depth+3, reflow=False)
             self.emit("goto failed;", depth+3)
             self.emit("}", depth+2)
-            self.emit("asdl_seq_SET(%s, i, value);" % field.name, depth+2)
+            self.emit("asdl_seq_SET(%s, i, val);" % field.name, depth+2)
             self.emit("}", depth+1)
         else:
             self.emit("res = obj2ast_%s(tmp, &%s, arena);" %
@@ -630,6 +630,8 @@ typedef struct {
 static void
 ast_dealloc(AST_object *self)
 {
+    /* bpo-31095: UnTrack is needed before calling any callbacks */
+    PyObject_GC_UnTrack(self);
     Py_CLEAR(self->dict);
     Py_TYPE(self)->tp_free(self);
 }
index e0607ba..212211c 100644 (file)
@@ -512,6 +512,8 @@ typedef struct {
 static void
 ast_dealloc(AST_object *self)
 {
+    /* bpo-31095: UnTrack is needed before calling any callbacks */
+    PyObject_GC_UnTrack(self);
     Py_CLEAR(self->dict);
     Py_TYPE(self)->tp_free(self);
 }
@@ -3994,14 +3996,14 @@ obj2ast_mod(PyObject* obj, mod_ty* out, PyArena* arena)
             body = _Py_asdl_seq_new(len, arena);
             if (body == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                stmt_ty value;
-                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &value, arena);
+                stmt_ty val;
+                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "Module field \"body\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(body, i, value);
+                asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -4033,14 +4035,14 @@ obj2ast_mod(PyObject* obj, mod_ty* out, PyArena* arena)
             body = _Py_asdl_seq_new(len, arena);
             if (body == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                stmt_ty value;
-                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &value, arena);
+                stmt_ty val;
+                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "Interactive field \"body\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(body, i, value);
+                asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -4094,14 +4096,14 @@ obj2ast_mod(PyObject* obj, mod_ty* out, PyArena* arena)
             body = _Py_asdl_seq_new(len, arena);
             if (body == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                stmt_ty value;
-                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &value, arena);
+                stmt_ty val;
+                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "Suite field \"body\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(body, i, value);
+                asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -4201,14 +4203,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             body = _Py_asdl_seq_new(len, arena);
             if (body == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                stmt_ty value;
-                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &value, arena);
+                stmt_ty val;
+                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "FunctionDef field \"body\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(body, i, value);
+                asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -4229,14 +4231,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             decorator_list = _Py_asdl_seq_new(len, arena);
             if (decorator_list == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                expr_ty value;
-                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &value, arena);
+                expr_ty val;
+                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "FunctionDef field \"decorator_list\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(decorator_list, i, value);
+                asdl_seq_SET(decorator_list, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -4305,14 +4307,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             body = _Py_asdl_seq_new(len, arena);
             if (body == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                stmt_ty value;
-                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &value, arena);
+                stmt_ty val;
+                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "AsyncFunctionDef field \"body\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(body, i, value);
+                asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -4333,14 +4335,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             decorator_list = _Py_asdl_seq_new(len, arena);
             if (decorator_list == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                expr_ty value;
-                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &value, arena);
+                expr_ty val;
+                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "AsyncFunctionDef field \"decorator_list\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(decorator_list, i, value);
+                asdl_seq_SET(decorator_list, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -4398,14 +4400,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             bases = _Py_asdl_seq_new(len, arena);
             if (bases == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                expr_ty value;
-                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &value, arena);
+                expr_ty val;
+                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "ClassDef field \"bases\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(bases, i, value);
+                asdl_seq_SET(bases, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -4426,14 +4428,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             keywords = _Py_asdl_seq_new(len, arena);
             if (keywords == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                keyword_ty value;
-                res = obj2ast_keyword(PyList_GET_ITEM(tmp, i), &value, arena);
+                keyword_ty val;
+                res = obj2ast_keyword(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "ClassDef field \"keywords\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(keywords, i, value);
+                asdl_seq_SET(keywords, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -4454,14 +4456,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             body = _Py_asdl_seq_new(len, arena);
             if (body == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                stmt_ty value;
-                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &value, arena);
+                stmt_ty val;
+                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "ClassDef field \"body\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(body, i, value);
+                asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -4482,14 +4484,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             decorator_list = _Py_asdl_seq_new(len, arena);
             if (decorator_list == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                expr_ty value;
-                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &value, arena);
+                expr_ty val;
+                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "ClassDef field \"decorator_list\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(decorator_list, i, value);
+                asdl_seq_SET(decorator_list, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -4543,14 +4545,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             targets = _Py_asdl_seq_new(len, arena);
             if (targets == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                expr_ty value;
-                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &value, arena);
+                expr_ty val;
+                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "Delete field \"targets\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(targets, i, value);
+                asdl_seq_SET(targets, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -4583,14 +4585,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             targets = _Py_asdl_seq_new(len, arena);
             if (targets == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                expr_ty value;
-                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &value, arena);
+                expr_ty val;
+                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "Assign field \"targets\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(targets, i, value);
+                asdl_seq_SET(targets, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -4762,14 +4764,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             body = _Py_asdl_seq_new(len, arena);
             if (body == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                stmt_ty value;
-                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &value, arena);
+                stmt_ty val;
+                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "For field \"body\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(body, i, value);
+                asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -4790,14 +4792,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             orelse = _Py_asdl_seq_new(len, arena);
             if (orelse == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                stmt_ty value;
-                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &value, arena);
+                stmt_ty val;
+                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "For field \"orelse\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(orelse, i, value);
+                asdl_seq_SET(orelse, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -4854,14 +4856,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             body = _Py_asdl_seq_new(len, arena);
             if (body == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                stmt_ty value;
-                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &value, arena);
+                stmt_ty val;
+                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "AsyncFor field \"body\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(body, i, value);
+                asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -4882,14 +4884,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             orelse = _Py_asdl_seq_new(len, arena);
             if (orelse == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                stmt_ty value;
-                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &value, arena);
+                stmt_ty val;
+                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "AsyncFor field \"orelse\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(orelse, i, value);
+                asdl_seq_SET(orelse, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -4934,14 +4936,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             body = _Py_asdl_seq_new(len, arena);
             if (body == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                stmt_ty value;
-                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &value, arena);
+                stmt_ty val;
+                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "While field \"body\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(body, i, value);
+                asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -4962,14 +4964,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             orelse = _Py_asdl_seq_new(len, arena);
             if (orelse == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                stmt_ty value;
-                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &value, arena);
+                stmt_ty val;
+                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "While field \"orelse\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(orelse, i, value);
+                asdl_seq_SET(orelse, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -5014,14 +5016,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             body = _Py_asdl_seq_new(len, arena);
             if (body == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                stmt_ty value;
-                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &value, arena);
+                stmt_ty val;
+                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "If field \"body\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(body, i, value);
+                asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -5042,14 +5044,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             orelse = _Py_asdl_seq_new(len, arena);
             if (orelse == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                stmt_ty value;
-                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &value, arena);
+                stmt_ty val;
+                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "If field \"orelse\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(orelse, i, value);
+                asdl_seq_SET(orelse, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -5082,14 +5084,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             items = _Py_asdl_seq_new(len, arena);
             if (items == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                withitem_ty value;
-                res = obj2ast_withitem(PyList_GET_ITEM(tmp, i), &value, arena);
+                withitem_ty val;
+                res = obj2ast_withitem(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "With field \"items\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(items, i, value);
+                asdl_seq_SET(items, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -5110,14 +5112,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             body = _Py_asdl_seq_new(len, arena);
             if (body == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                stmt_ty value;
-                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &value, arena);
+                stmt_ty val;
+                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "With field \"body\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(body, i, value);
+                asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -5150,14 +5152,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             items = _Py_asdl_seq_new(len, arena);
             if (items == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                withitem_ty value;
-                res = obj2ast_withitem(PyList_GET_ITEM(tmp, i), &value, arena);
+                withitem_ty val;
+                res = obj2ast_withitem(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "AsyncWith field \"items\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(items, i, value);
+                asdl_seq_SET(items, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -5178,14 +5180,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             body = _Py_asdl_seq_new(len, arena);
             if (body == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                stmt_ty value;
-                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &value, arena);
+                stmt_ty val;
+                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "AsyncWith field \"body\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(body, i, value);
+                asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -5252,14 +5254,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             body = _Py_asdl_seq_new(len, arena);
             if (body == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                stmt_ty value;
-                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &value, arena);
+                stmt_ty val;
+                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "Try field \"body\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(body, i, value);
+                asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -5280,14 +5282,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             handlers = _Py_asdl_seq_new(len, arena);
             if (handlers == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                excepthandler_ty value;
-                res = obj2ast_excepthandler(PyList_GET_ITEM(tmp, i), &value, arena);
+                excepthandler_ty val;
+                res = obj2ast_excepthandler(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "Try field \"handlers\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(handlers, i, value);
+                asdl_seq_SET(handlers, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -5308,14 +5310,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             orelse = _Py_asdl_seq_new(len, arena);
             if (orelse == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                stmt_ty value;
-                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &value, arena);
+                stmt_ty val;
+                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "Try field \"orelse\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(orelse, i, value);
+                asdl_seq_SET(orelse, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -5336,14 +5338,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             finalbody = _Py_asdl_seq_new(len, arena);
             if (finalbody == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                stmt_ty value;
-                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &value, arena);
+                stmt_ty val;
+                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "Try field \"finalbody\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(finalbody, i, value);
+                asdl_seq_SET(finalbody, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -5409,14 +5411,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             names = _Py_asdl_seq_new(len, arena);
             if (names == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                alias_ty value;
-                res = obj2ast_alias(PyList_GET_ITEM(tmp, i), &value, arena);
+                alias_ty val;
+                res = obj2ast_alias(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "Import field \"names\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(names, i, value);
+                asdl_seq_SET(names, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -5460,14 +5462,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             names = _Py_asdl_seq_new(len, arena);
             if (names == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                alias_ty value;
-                res = obj2ast_alias(PyList_GET_ITEM(tmp, i), &value, arena);
+                alias_ty val;
+                res = obj2ast_alias(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "ImportFrom field \"names\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(names, i, value);
+                asdl_seq_SET(names, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -5509,14 +5511,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             names = _Py_asdl_seq_new(len, arena);
             if (names == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                identifier value;
-                res = obj2ast_identifier(PyList_GET_ITEM(tmp, i), &value, arena);
+                identifier val;
+                res = obj2ast_identifier(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "Global field \"names\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(names, i, value);
+                asdl_seq_SET(names, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -5548,14 +5550,14 @@ obj2ast_stmt(PyObject* obj, stmt_ty* out, PyArena* arena)
             names = _Py_asdl_seq_new(len, arena);
             if (names == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                identifier value;
-                res = obj2ast_identifier(PyList_GET_ITEM(tmp, i), &value, arena);
+                identifier val;
+                res = obj2ast_identifier(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "Nonlocal field \"names\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(names, i, value);
+                asdl_seq_SET(names, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -5693,14 +5695,14 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
             values = _Py_asdl_seq_new(len, arena);
             if (values == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                expr_ty value;
-                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &value, arena);
+                expr_ty val;
+                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "BoolOp field \"values\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(values, i, value);
+                asdl_seq_SET(values, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -5893,14 +5895,14 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
             keys = _Py_asdl_seq_new(len, arena);
             if (keys == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                expr_ty value;
-                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &value, arena);
+                expr_ty val;
+                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "Dict field \"keys\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(keys, i, value);
+                asdl_seq_SET(keys, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -5921,14 +5923,14 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
             values = _Py_asdl_seq_new(len, arena);
             if (values == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                expr_ty value;
-                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &value, arena);
+                expr_ty val;
+                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "Dict field \"values\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(values, i, value);
+                asdl_seq_SET(values, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -5960,14 +5962,14 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
             elts = _Py_asdl_seq_new(len, arena);
             if (elts == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                expr_ty value;
-                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &value, arena);
+                expr_ty val;
+                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "Set field \"elts\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(elts, i, value);
+                asdl_seq_SET(elts, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -6011,14 +6013,14 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
             generators = _Py_asdl_seq_new(len, arena);
             if (generators == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                comprehension_ty value;
-                res = obj2ast_comprehension(PyList_GET_ITEM(tmp, i), &value, arena);
+                comprehension_ty val;
+                res = obj2ast_comprehension(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "ListComp field \"generators\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(generators, i, value);
+                asdl_seq_SET(generators, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -6062,14 +6064,14 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
             generators = _Py_asdl_seq_new(len, arena);
             if (generators == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                comprehension_ty value;
-                res = obj2ast_comprehension(PyList_GET_ITEM(tmp, i), &value, arena);
+                comprehension_ty val;
+                res = obj2ast_comprehension(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "SetComp field \"generators\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(generators, i, value);
+                asdl_seq_SET(generators, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -6125,14 +6127,14 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
             generators = _Py_asdl_seq_new(len, arena);
             if (generators == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                comprehension_ty value;
-                res = obj2ast_comprehension(PyList_GET_ITEM(tmp, i), &value, arena);
+                comprehension_ty val;
+                res = obj2ast_comprehension(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "DictComp field \"generators\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(generators, i, value);
+                asdl_seq_SET(generators, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -6176,14 +6178,14 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
             generators = _Py_asdl_seq_new(len, arena);
             if (generators == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                comprehension_ty value;
-                res = obj2ast_comprehension(PyList_GET_ITEM(tmp, i), &value, arena);
+                comprehension_ty val;
+                res = obj2ast_comprehension(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "GeneratorExp field \"generators\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(generators, i, value);
+                asdl_seq_SET(generators, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -6293,14 +6295,14 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
             ops = _Py_asdl_int_seq_new(len, arena);
             if (ops == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                cmpop_ty value;
-                res = obj2ast_cmpop(PyList_GET_ITEM(tmp, i), &value, arena);
+                cmpop_ty val;
+                res = obj2ast_cmpop(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "Compare field \"ops\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(ops, i, value);
+                asdl_seq_SET(ops, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -6321,14 +6323,14 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
             comparators = _Py_asdl_seq_new(len, arena);
             if (comparators == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                expr_ty value;
-                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &value, arena);
+                expr_ty val;
+                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "Compare field \"comparators\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(comparators, i, value);
+                asdl_seq_SET(comparators, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -6373,14 +6375,14 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
             args = _Py_asdl_seq_new(len, arena);
             if (args == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                expr_ty value;
-                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &value, arena);
+                expr_ty val;
+                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "Call field \"args\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(args, i, value);
+                asdl_seq_SET(args, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -6401,14 +6403,14 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
             keywords = _Py_asdl_seq_new(len, arena);
             if (keywords == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                keyword_ty value;
-                res = obj2ast_keyword(PyList_GET_ITEM(tmp, i), &value, arena);
+                keyword_ty val;
+                res = obj2ast_keyword(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "Call field \"keywords\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(keywords, i, value);
+                asdl_seq_SET(keywords, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -6529,14 +6531,14 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
             values = _Py_asdl_seq_new(len, arena);
             if (values == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                expr_ty value;
-                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &value, arena);
+                expr_ty val;
+                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "JoinedStr field \"values\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(values, i, value);
+                asdl_seq_SET(values, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -6805,14 +6807,14 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
             elts = _Py_asdl_seq_new(len, arena);
             if (elts == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                expr_ty value;
-                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &value, arena);
+                expr_ty val;
+                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "List field \"elts\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(elts, i, value);
+                asdl_seq_SET(elts, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -6856,14 +6858,14 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
             elts = _Py_asdl_seq_new(len, arena);
             if (elts == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                expr_ty value;
-                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &value, arena);
+                expr_ty val;
+                res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "Tuple field \"elts\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(elts, i, value);
+                asdl_seq_SET(elts, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -7025,14 +7027,14 @@ obj2ast_slice(PyObject* obj, slice_ty* out, PyArena* arena)
             dims = _Py_asdl_seq_new(len, arena);
             if (dims == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                slice_ty value;
-                res = obj2ast_slice(PyList_GET_ITEM(tmp, i), &value, arena);
+                slice_ty val;
+                res = obj2ast_slice(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "ExtSlice field \"dims\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(dims, i, value);
+                asdl_seq_SET(dims, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -7389,14 +7391,14 @@ obj2ast_comprehension(PyObject* obj, comprehension_ty* out, PyArena* arena)
         ifs = _Py_asdl_seq_new(len, arena);
         if (ifs == NULL) goto failed;
         for (i = 0; i < len; i++) {
-            expr_ty value;
-            res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &value, arena);
+            expr_ty val;
+            res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &val, arena);
             if (res != 0) goto failed;
             if (len != PyList_GET_SIZE(tmp)) {
                 PyErr_SetString(PyExc_RuntimeError, "comprehension field \"ifs\" changed size during iteration");
                 goto failed;
             }
-            asdl_seq_SET(ifs, i, value);
+            asdl_seq_SET(ifs, i, val);
         }
         Py_CLEAR(tmp);
     } else {
@@ -7499,14 +7501,14 @@ obj2ast_excepthandler(PyObject* obj, excepthandler_ty* out, PyArena* arena)
             body = _Py_asdl_seq_new(len, arena);
             if (body == NULL) goto failed;
             for (i = 0; i < len; i++) {
-                stmt_ty value;
-                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &value, arena);
+                stmt_ty val;
+                res = obj2ast_stmt(PyList_GET_ITEM(tmp, i), &val, arena);
                 if (res != 0) goto failed;
                 if (len != PyList_GET_SIZE(tmp)) {
                     PyErr_SetString(PyExc_RuntimeError, "ExceptHandler field \"body\" changed size during iteration");
                     goto failed;
                 }
-                asdl_seq_SET(body, i, value);
+                asdl_seq_SET(body, i, val);
             }
             Py_CLEAR(tmp);
         } else {
@@ -7549,14 +7551,14 @@ obj2ast_arguments(PyObject* obj, arguments_ty* out, PyArena* arena)
         args = _Py_asdl_seq_new(len, arena);
         if (args == NULL) goto failed;
         for (i = 0; i < len; i++) {
-            arg_ty value;
-            res = obj2ast_arg(PyList_GET_ITEM(tmp, i), &value, arena);
+            arg_ty val;
+            res = obj2ast_arg(PyList_GET_ITEM(tmp, i), &val, arena);
             if (res != 0) goto failed;
             if (len != PyList_GET_SIZE(tmp)) {
                 PyErr_SetString(PyExc_RuntimeError, "arguments field \"args\" changed size during iteration");
                 goto failed;
             }
-            asdl_seq_SET(args, i, value);
+            asdl_seq_SET(args, i, val);
         }
         Py_CLEAR(tmp);
     } else {
@@ -7587,14 +7589,14 @@ obj2ast_arguments(PyObject* obj, arguments_ty* out, PyArena* arena)
         kwonlyargs = _Py_asdl_seq_new(len, arena);
         if (kwonlyargs == NULL) goto failed;
         for (i = 0; i < len; i++) {
-            arg_ty value;
-            res = obj2ast_arg(PyList_GET_ITEM(tmp, i), &value, arena);
+            arg_ty val;
+            res = obj2ast_arg(PyList_GET_ITEM(tmp, i), &val, arena);
             if (res != 0) goto failed;
             if (len != PyList_GET_SIZE(tmp)) {
                 PyErr_SetString(PyExc_RuntimeError, "arguments field \"kwonlyargs\" changed size during iteration");
                 goto failed;
             }
-            asdl_seq_SET(kwonlyargs, i, value);
+            asdl_seq_SET(kwonlyargs, i, val);
         }
         Py_CLEAR(tmp);
     } else {
@@ -7615,14 +7617,14 @@ obj2ast_arguments(PyObject* obj, arguments_ty* out, PyArena* arena)
         kw_defaults = _Py_asdl_seq_new(len, arena);
         if (kw_defaults == NULL) goto failed;
         for (i = 0; i < len; i++) {
-            expr_ty value;
-            res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &value, arena);
+            expr_ty val;
+            res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &val, arena);
             if (res != 0) goto failed;
             if (len != PyList_GET_SIZE(tmp)) {
                 PyErr_SetString(PyExc_RuntimeError, "arguments field \"kw_defaults\" changed size during iteration");
                 goto failed;
             }
-            asdl_seq_SET(kw_defaults, i, value);
+            asdl_seq_SET(kw_defaults, i, val);
         }
         Py_CLEAR(tmp);
     } else {
@@ -7653,14 +7655,14 @@ obj2ast_arguments(PyObject* obj, arguments_ty* out, PyArena* arena)
         defaults = _Py_asdl_seq_new(len, arena);
         if (defaults == NULL) goto failed;
         for (i = 0; i < len; i++) {
-            expr_ty value;
-            res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &value, arena);
+            expr_ty val;
+            res = obj2ast_expr(PyList_GET_ITEM(tmp, i), &val, arena);
             if (res != 0) goto failed;
             if (len != PyList_GET_SIZE(tmp)) {
                 PyErr_SetString(PyExc_RuntimeError, "arguments field \"defaults\" changed size during iteration");
                 goto failed;
             }
-            asdl_seq_SET(defaults, i, value);
+            asdl_seq_SET(defaults, i, val);
         }
         Py_CLEAR(tmp);
     } else {
index 6cfae77..7270d2c 100644 (file)
@@ -94,6 +94,12 @@ get_once_registry(void)
             return NULL;
         return _once_registry;
     }
+    if (!PyDict_Check(registry)) {
+        PyErr_SetString(PyExc_TypeError,
+                        "warnings.onceregistry must be a dict");
+        Py_DECREF(registry);
+        return NULL;
+    }
     Py_DECREF(_once_registry);
     _once_registry = registry;
     return registry;
@@ -112,7 +118,14 @@ get_default_action(void)
         }
         return _default_action;
     }
-
+    if (!PyUnicode_Check(default_action)) {
+        PyErr_Format(PyExc_TypeError,
+                     MODULE_NAME ".defaultaction must be a string, "
+                     "not '%.200s'",
+                     Py_TYPE(default_action)->tp_name);
+        Py_DECREF(default_action);
+        return NULL;
+    }
     Py_DECREF(_default_action);
     _default_action = default_action;
     return default_action;
@@ -165,6 +178,14 @@ get_filter(PyObject *category, PyObject *text, Py_ssize_t lineno,
         mod = PyTuple_GET_ITEM(tmp_item, 3);
         ln_obj = PyTuple_GET_ITEM(tmp_item, 4);
 
+        if (!PyUnicode_Check(action)) {
+            PyErr_Format(PyExc_TypeError,
+                         "action must be a string, not '%.200s'",
+                         Py_TYPE(action)->tp_name);
+            Py_DECREF(tmp_item);
+            return NULL;
+        }
+
         good_msg = check_matched(msg, text);
         if (good_msg == -1) {
             Py_DECREF(tmp_item);
@@ -204,8 +225,6 @@ get_filter(PyObject *category, PyObject *text, Py_ssize_t lineno,
         return action;
     }
 
-    PyErr_SetString(PyExc_ValueError,
-                    MODULE_NAME ".defaultaction not found");
     return NULL;
 }
 
@@ -449,7 +468,7 @@ warn_explicit(PyObject *category, PyObject *message,
         Py_RETURN_NONE;
 
     if (registry && !PyDict_Check(registry) && (registry != Py_None)) {
-        PyErr_SetString(PyExc_TypeError, "'registry' must be a dict");
+        PyErr_SetString(PyExc_TypeError, "'registry' must be a dict or None");
         return NULL;
     }
 
index 8d53736..aa4acc9 100644 (file)
@@ -8,6 +8,7 @@
 #include "node.h"
 #include "ast.h"
 #include "token.h"
+#include "pythonrun.h"
 
 #include <assert.h>
 
@@ -1174,6 +1175,7 @@ ast_for_comp_op(struct compiling *c, const node *n)
                     return In;
                 if (strcmp(STR(n), "is") == 0)
                     return Is;
+                /* fall through */
             default:
                 PyErr_Format(PyExc_SystemError, "invalid comp_op: %s",
                              STR(n));
@@ -1188,6 +1190,7 @@ ast_for_comp_op(struct compiling *c, const node *n)
                     return NotIn;
                 if (strcmp(STR(CHILD(n, 0)), "is") == 0)
                     return IsNot;
+                /* fall through */
             default:
                 PyErr_Format(PyExc_SystemError, "invalid comp_op: %s %s",
                              STR(CHILD(n, 0)), STR(CHILD(n, 1)));
@@ -3148,6 +3151,7 @@ ast_for_flow_stmt(struct compiling *c, const node *n)
                 }
                 return Raise(expression, cause, LINENO(n), n->n_col_offset, c->c_arena);
             }
+            /* fall through */
         default:
             PyErr_Format(PyExc_SystemError,
                          "unexpected flow_stmt: %d", TYPE(ch));
@@ -4240,6 +4244,55 @@ decode_bytes_with_escapes(struct compiling *c, const node *n, const char *s,
     return result;
 }
 
+/* Shift locations for the given node and all its children by adding `lineno`
+   and `col_offset` to existing locations. */
+static void fstring_shift_node_locations(node *n, int lineno, int col_offset)
+{
+    n->n_col_offset = n->n_col_offset + col_offset;
+    for (int i = 0; i < NCH(n); ++i) {
+        if (n->n_lineno && n->n_lineno < CHILD(n, i)->n_lineno) {
+            /* Shifting column offsets unnecessary if there's been newlines. */
+            col_offset = 0;
+        }
+        fstring_shift_node_locations(CHILD(n, i), lineno, col_offset);
+    }
+    n->n_lineno = n->n_lineno + lineno;
+}
+
+/* Fix locations for the given node and its children.
+
+   `parent` is the enclosing node.
+   `n` is the node which locations are going to be fixed relative to parent.
+   `expr_str` is the child node's string representation, incuding braces.
+*/
+static void
+fstring_fix_node_location(const node *parent, node *n, char *expr_str)
+{
+    char *substr = NULL;
+    char *start;
+    int lines = LINENO(parent) - 1;
+    int cols = parent->n_col_offset;
+    /* Find the full fstring to fix location information in `n`. */
+    while (parent && parent->n_type != STRING)
+        parent = parent->n_child;
+    if (parent && parent->n_str) {
+        substr = strstr(parent->n_str, expr_str);
+        if (substr) {
+            start = substr;
+            while (start > parent->n_str) {
+                if (start[0] == '\n')
+                    break;
+                start--;
+            }
+            cols += substr - start;
+            /* Fix lineno in mulitline strings. */
+            while ((substr = strchr(substr + 1, '\n')))
+                lines--;
+        }
+    }
+    fstring_shift_node_locations(n, lines, cols);
+}
+
 /* Compile this expression in to an expr_ty.  Add parens around the
    expression, in order to allow leading spaces in the expression. */
 static expr_ty
@@ -4248,6 +4301,7 @@ fstring_compile_expr(const char *expr_start, const char *expr_end,
 
 {
     PyCompilerFlags cf;
+    node *mod_n;
     mod_ty mod;
     char *str;
     Py_ssize_t len;
@@ -4257,9 +4311,10 @@ fstring_compile_expr(const char *expr_start, const char *expr_end,
     assert(*(expr_start-1) == '{');
     assert(*expr_end == '}' || *expr_end == '!' || *expr_end == ':');
 
-    /* If the substring is all whitespace, it's an error.  We need to catch
-       this here, and not when we call PyParser_ASTFromString, because turning
-       the expression '' in to '()' would go from being invalid to valid. */
+    /* If the substring is all whitespace, it's an error.  We need to catch this
+       here, and not when we call PyParser_SimpleParseStringFlagsFilename,
+       because turning the expression '' in to '()' would go from being invalid
+       to valid. */
     for (s = expr_start; s != expr_end; s++) {
         char c = *s;
         /* The Python parser ignores only the following whitespace
@@ -4285,9 +4340,19 @@ fstring_compile_expr(const char *expr_start, const char *expr_end,
     str[len+2] = 0;
 
     cf.cf_flags = PyCF_ONLY_AST;
-    mod = PyParser_ASTFromString(str, "<fstring>",
-                                 Py_eval_input, &cf, c->c_arena);
+    mod_n = PyParser_SimpleParseStringFlagsFilename(str, "<fstring>",
+                                                    Py_eval_input, 0);
+    if (!mod_n) {
+        PyMem_RawFree(str);
+        return NULL;
+    }
+    /* Reuse str to find the correct column offset. */
+    str[0] = '{';
+    str[len+1] = '}';
+    fstring_fix_node_location(n, mod_n, str);
+    mod = PyAST_FromNode(mod_n, &cf, "<fstring>", c->c_arena);
     PyMem_RawFree(str);
+    PyNode_Free(mod_n);
     if (!mod)
         return NULL;
     return mod->v.Expression.body;
index 597e26e..911e2ba 100644 (file)
@@ -588,12 +588,14 @@ format as builtin_format
 
 Return value.__format__(format_spec)
 
-format_spec defaults to the empty string
+format_spec defaults to the empty string.
+See the Format Specification Mini-Language section of help('FORMATTING') for
+details.
 [clinic start generated code]*/
 
 static PyObject *
 builtin_format_impl(PyObject *module, PyObject *value, PyObject *format_spec)
-/*[clinic end generated code: output=2f40bdfa4954b077 input=6325e751a1b29b86]*/
+/*[clinic end generated code: output=2f40bdfa4954b077 input=88339c93ea522b33]*/
 {
     return PyObject_Format(value, format_spec);
 }
index ea79f5f..b6ad444 100644 (file)
@@ -64,6 +64,8 @@ static void format_exc_unbound(PyCodeObject *co, int oparg);
 static PyObject * unicode_concatenate(PyObject *, PyObject *,
                                       PyFrameObject *, const _Py_CODEUNIT *);
 static PyObject * special_lookup(PyObject *, _Py_Identifier *);
+static int check_args_iterable(PyObject *func, PyObject *vararg);
+static void format_kwargs_mapping_error(PyObject *func, PyObject *kwargs);
 
 #define NAME_ERROR_MSG \
     "name '%.200s' is not defined"
@@ -195,6 +197,15 @@ PyEval_GetCallStats(PyObject *self)
     do { pending_async_exc = 0; COMPUTE_EVAL_BREAKER(); } while (0)
 
 
+/* This single variable consolidates all requests to break out of the fast path
+   in the eval loop. */
+static _Py_atomic_int eval_breaker = {0};
+/* Request for running pending calls. */
+static _Py_atomic_int pendingcalls_to_do = {0};
+/* Request for looking at the `async_exc` field of the current thread state.
+   Guarded by the GIL. */
+static int pending_async_exc = 0;
+
 #ifdef WITH_THREAD
 
 #ifdef HAVE_ERRNO_H
@@ -204,16 +215,8 @@ PyEval_GetCallStats(PyObject *self)
 
 static PyThread_type_lock pending_lock = 0; /* for pending calls */
 static long main_thread = 0;
-/* This single variable consolidates all requests to break out of the fast path
-   in the eval loop. */
-static _Py_atomic_int eval_breaker = {0};
 /* Request for dropping the GIL */
 static _Py_atomic_int gil_drop_request = {0};
-/* Request for running pending calls. */
-static _Py_atomic_int pendingcalls_to_do = {0};
-/* Request for looking at the `async_exc` field of the current thread state.
-   Guarded by the GIL. */
-static int pending_async_exc = 0;
 
 #include "ceval_gil.h"
 
@@ -326,9 +329,6 @@ PyEval_ReInitThreads(void)
     _PyThreadState_DeleteExcept(current_tstate);
 }
 
-#else
-static _Py_atomic_int eval_breaker = {0};
-static int pending_async_exc = 0;
 #endif /* WITH_THREAD */
 
 /* This function is used to signal that async exceptions are waiting to be
@@ -403,6 +403,15 @@ PyEval_RestoreThread(PyThreadState *tstate)
 #endif
 */
 
+void
+_PyEval_SignalReceived(void)
+{
+    /* bpo-30703: Function called when the C signal handler of Python gets a
+       signal. We cannot queue a callback using Py_AddPendingCall() since
+       that function is not async-signal-safe. */
+    SIGNAL_PENDING_CALLS();
+}
+
 #ifdef WITH_THREAD
 
 /* The WITH_THREAD implementation is thread-safe.  It allows
@@ -467,6 +476,8 @@ Py_MakePendingCalls(void)
     int i;
     int r = 0;
 
+    assert(PyGILState_Check());
+
     if (!pending_lock) {
         /* initial allocation of the lock */
         pending_lock = PyThread_allocate_lock();
@@ -481,6 +492,16 @@ Py_MakePendingCalls(void)
     if (busy)
         return 0;
     busy = 1;
+    /* unsignal before starting to call callbacks, so that any callback
+       added in-between re-signals */
+    UNSIGNAL_PENDING_CALLS();
+
+    /* Python signal handler doesn't really queue a callback: it only signals
+       that a signal was received, see _PyEval_SignalReceived(). */
+    if (PyErr_CheckSignals() < 0) {
+        goto error;
+    }
+
     /* perform a bounded number of calls, in case of recursion */
     for (i=0; i<NPENDINGCALLS; i++) {
         int j;
@@ -497,20 +518,23 @@ Py_MakePendingCalls(void)
             arg = pendingcalls[j].arg;
             pendingfirst = (j + 1) % NPENDINGCALLS;
         }
-        if (pendingfirst != pendinglast)
-            SIGNAL_PENDING_CALLS();
-        else
-            UNSIGNAL_PENDING_CALLS();
         PyThread_release_lock(pending_lock);
         /* having released the lock, perform the callback */
         if (func == NULL)
             break;
         r = func(arg);
-        if (r)
-            break;
+        if (r) {
+            goto error;
+        }
     }
+
     busy = 0;
     return r;
+
+error:
+    busy = 0;
+    SIGNAL_PENDING_CALLS(); /* We're not done yet */
+    return -1;
 }
 
 #else /* if ! defined WITH_THREAD */
@@ -545,7 +569,6 @@ static struct {
 } pendingcalls[NPENDINGCALLS];
 static volatile int pendingfirst = 0;
 static volatile int pendinglast = 0;
-static _Py_atomic_int pendingcalls_to_do = {0};
 
 int
 Py_AddPendingCall(int (*func)(void *), void *arg)
@@ -579,7 +602,16 @@ Py_MakePendingCalls(void)
     if (busy)
         return 0;
     busy = 1;
+
+    /* unsignal before starting to call callbacks, so that any callback
+       added in-between re-signals */
     UNSIGNAL_PENDING_CALLS();
+    /* Python signal handler doesn't really queue a callback: it only signals
+       that a signal was received, see _PyEval_SignalReceived(). */
+    if (PyErr_CheckSignals() < 0) {
+        goto error;
+    }
+
     for (;;) {
         int i;
         int (*func)(void *);
@@ -591,13 +623,16 @@ Py_MakePendingCalls(void)
         arg = pendingcalls[i].arg;
         pendingfirst = (i + 1) % NPENDINGCALLS;
         if (func(arg) < 0) {
-            busy = 0;
-            SIGNAL_PENDING_CALLS(); /* We're not done yet */
-            return -1;
+            goto error;
         }
     }
     busy = 0;
     return 0;
+
+error:
+    busy = 0;
+    SIGNAL_PENDING_CALLS(); /* We're not done yet */
+    return -1;
 }
 
 #endif /* WITH_THREAD */
@@ -1840,9 +1875,11 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
             switch (oparg) {
             case 2:
                 cause = POP(); /* cause */
+                /* fall through */
             case 1:
                 exc = POP(); /* exc */
-            case 0: /* Fallthrough */
+                /* fall through */
+            case 0:
                 if (do_raise(exc, cause)) {
                     why = WHY_EXCEPTION;
                     goto fast_block_end;
@@ -2545,14 +2582,9 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
                 none_val = _PyList_Extend((PyListObject *)sum, PEEK(i));
                 if (none_val == NULL) {
                     if (opcode == BUILD_TUPLE_UNPACK_WITH_CALL &&
-                        PyErr_ExceptionMatches(PyExc_TypeError)) {
-                        PyObject *func = PEEK(1 + oparg);
-                        PyErr_Format(PyExc_TypeError,
-                                "%.200s%.200s argument after * "
-                                "must be an iterable, not %.200s",
-                                PyEval_GetFuncName(func),
-                                PyEval_GetFuncDesc(func),
-                                PEEK(i)->ob_type->tp_name);
+                        PyErr_ExceptionMatches(PyExc_TypeError))
+                    {
+                        check_args_iterable(PEEK(1 + oparg), PEEK(i));
                     }
                     Py_DECREF(sum);
                     goto error;
@@ -2765,12 +2797,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
                 if (_PyDict_MergeEx(sum, arg, 2) < 0) {
                     PyObject *func = PEEK(2 + oparg);
                     if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                        PyErr_Format(PyExc_TypeError,
-                                "%.200s%.200s argument after ** "
-                                "must be a mapping, not %.200s",
-                                PyEval_GetFuncName(func),
-                                PyEval_GetFuncDesc(func),
-                                arg->ob_type->tp_name);
+                        format_kwargs_mapping_error(func, arg);
                     }
                     else if (PyErr_ExceptionMatches(PyExc_KeyError)) {
                         PyObject *exc, *val, *tb;
@@ -3337,13 +3364,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
                          * is not a mapping.
                          */
                         if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                            func = SECOND();
-                            PyErr_Format(PyExc_TypeError,
-                                         "%.200s%.200s argument after ** "
-                                         "must be a mapping, not %.200s",
-                                         PyEval_GetFuncName(func),
-                                         PyEval_GetFuncDesc(func),
-                                         kwargs->ob_type->tp_name);
+                            format_kwargs_mapping_error(SECOND(), kwargs);
                         }
                         Py_DECREF(kwargs);
                         goto error;
@@ -3356,14 +3377,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
             callargs = POP();
             func = TOP();
             if (!PyTuple_CheckExact(callargs)) {
-                if (Py_TYPE(callargs)->tp_iter == NULL &&
-                        !PySequence_Check(callargs)) {
-                    PyErr_Format(PyExc_TypeError,
-                                 "%.200s%.200s argument after * "
-                                 "must be an iterable, not %.200s",
-                                 PyEval_GetFuncName(func),
-                                 PyEval_GetFuncDesc(func),
-                                 callargs->ob_type->tp_name);
+                if (check_args_iterable(func, callargs) < 0) {
                     Py_DECREF(callargs);
                     goto error;
                 }
@@ -4159,7 +4173,8 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
 {
     return _PyEval_EvalCodeWithName(_co, globals, locals,
                                     args, argcount,
-                                    kws, kws + 1, kwcount, 2,
+                                    kws, kws != NULL ? kws + 1 : NULL,
+                                    kwcount, 2,
                                     defs, defcount,
                                     kwdefs, closure,
                                     NULL, NULL);
@@ -5041,7 +5056,7 @@ _PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs,
 
     result = _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL,
                                       args, nargs,
-                                      k, k + 1, nk, 2,
+                                      k, k != NULL ? k + 1 : NULL, nk, 2,
                                       d, nd, kwdefs,
                                       closure, name, qualname);
     Py_XDECREF(kwtuple);
@@ -5294,13 +5309,16 @@ import_all_from(PyObject *locals, PyObject *v)
                 PyErr_Clear();
             break;
         }
-        if (skip_leading_underscores &&
-            PyUnicode_Check(name) &&
-            PyUnicode_READY(name) != -1 &&
-            PyUnicode_READ_CHAR(name, 0) == '_')
-        {
-            Py_DECREF(name);
-            continue;
+        if (skip_leading_underscores && PyUnicode_Check(name)) {
+            if (PyUnicode_READY(name) == -1) {
+                Py_DECREF(name);
+                err = -1;
+                break;
+            }
+            if (PyUnicode_READ_CHAR(name, 0) == '_') {
+                Py_DECREF(name);
+                continue;
+            }
         }
         value = PyObject_GetAttr(v, name);
         if (value == NULL)
@@ -5318,6 +5336,32 @@ import_all_from(PyObject *locals, PyObject *v)
     return err;
 }
 
+static int
+check_args_iterable(PyObject *func, PyObject *args)
+{
+    if (args->ob_type->tp_iter == NULL && !PySequence_Check(args)) {
+        PyErr_Format(PyExc_TypeError,
+                     "%.200s%.200s argument after * "
+                     "must be an iterable, not %.200s",
+                     PyEval_GetFuncName(func),
+                     PyEval_GetFuncDesc(func),
+                     args->ob_type->tp_name);
+        return -1;
+    }
+    return 0;
+}
+
+static void
+format_kwargs_mapping_error(PyObject *func, PyObject *kwargs)
+{
+    PyErr_Format(PyExc_TypeError,
+                 "%.200s%.200s argument after ** "
+                 "must be a mapping, not %.200s",
+                 PyEval_GetFuncName(func),
+                 PyEval_GetFuncDesc(func),
+                 kwargs->ob_type->tp_name);
+}
+
 static void
 format_exc_check_arg(PyObject *exc, const char *format_str, PyObject *obj)
 {
index c88deef..37ce794 100644 (file)
@@ -77,7 +77,9 @@ PyDoc_STRVAR(builtin_format__doc__,
 "\n"
 "Return value.__format__(format_spec)\n"
 "\n"
-"format_spec defaults to the empty string");
+"format_spec defaults to the empty string.\n"
+"See the Format Specification Mini-Language section of help(\'FORMATTING\') for\n"
+"details.");
 
 #define BUILTIN_FORMAT_METHODDEF    \
     {"format", (PyCFunction)builtin_format, METH_VARARGS, builtin_format__doc__},
@@ -674,4 +676,4 @@ builtin_issubclass(PyObject *module, PyObject *args)
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=63483deb75805f7c input=a9049054013a1b77]*/
+/*[clinic end generated code: output=2ef82846acdfa0f5 input=a9049054013a1b77]*/
index 6255ec7..797a184 100644 (file)
@@ -4069,6 +4069,7 @@ expr_constant(struct compiler *c, expr_ty e)
         else if (o == Py_False)
             return 0;
     }
+    /* fall through */
     default:
         return -1;
     }
@@ -4361,13 +4362,13 @@ compiler_visit_expr(struct compiler *c, expr_ty e)
         switch (e->v.Attribute.ctx) {
         case AugLoad:
             ADDOP(c, DUP_TOP);
-            /* Fall through to load */
+            /* Fall through */
         case Load:
             ADDOP_NAME(c, LOAD_ATTR, e->v.Attribute.attr, names);
             break;
         case AugStore:
             ADDOP(c, ROT_TWO);
-            /* Fall through to save */
+            /* Fall through */
         case Store:
             ADDOP_NAME(c, STORE_ATTR, e->v.Attribute.attr, names);
             break;
index efcadc3..01ca9b0 100644 (file)
@@ -1454,7 +1454,7 @@ _Py_dg_strtod(const char *s00, char **se)
     switch (c) {
     case '-':
         sign = 1;
-        /* no break */
+        /* fall through */
     case '+':
         c = *++s;
     }
@@ -1523,7 +1523,7 @@ _Py_dg_strtod(const char *s00, char **se)
         switch (c) {
         case '-':
             esign = 1;
-            /* no break */
+            /* fall through */
         case '+':
             c = *++s;
         }
@@ -2441,7 +2441,7 @@ _Py_dg_dtoa(double dd, int mode, int ndigits,
         break;
     case 2:
         leftright = 0;
-        /* no break */
+        /* fall through */
     case 4:
         if (ndigits <= 0)
             ndigits = 1;
@@ -2449,7 +2449,7 @@ _Py_dg_dtoa(double dd, int mode, int ndigits,
         break;
     case 3:
         leftright = 0;
-        /* no break */
+        /* fall through */
     case 5:
         i = ndigits + k + 1;
         ilim = i;
index 05050cf..0fdf77f 100644 (file)
@@ -190,13 +190,13 @@ dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix,
 {
     dl_funcptr p;
     char funcname[258], *import_python;
-    wchar_t *wpathname;
+    const wchar_t *wpathname;
 
 #ifndef _DEBUG
     _Py_CheckPython3();
 #endif
 
-    wpathname = PyUnicode_AsUnicode(pathname);
+    wpathname = _PyUnicode_AsUnicode(pathname);
     if (wpathname == NULL)
         return NULL;
 
index 6095843..2f39f9d 100644 (file)
@@ -978,7 +978,7 @@ PyErr_WriteUnraisable(PyObject *obj)
     }
 
     moduleName = _PyObject_GetAttrId(t, &PyId___module__);
-    if (moduleName == NULL) {
+    if (moduleName == NULL || !PyUnicode_Check(moduleName)) {
         PyErr_Clear();
         if (PyFile_WriteString("<unknown>", f) < 0)
             goto done;
index f3764e4..97505e5 100644 (file)
@@ -711,21 +711,32 @@ _Py_stat(PyObject *path, struct stat *statbuf)
 #ifdef MS_WINDOWS
     int err;
     struct _stat wstatbuf;
-    wchar_t *wpath;
+    const wchar_t *wpath;
 
-    wpath = PyUnicode_AsUnicode(path);
+    wpath = _PyUnicode_AsUnicode(path);
     if (wpath == NULL)
         return -2;
+
     err = _wstat(wpath, &wstatbuf);
     if (!err)
         statbuf->st_mode = wstatbuf.st_mode;
     return err;
 #else
     int ret;
-    PyObject *bytes = PyUnicode_EncodeFSDefault(path);
+    PyObject *bytes;
+    char *cpath;
+
+    bytes = PyUnicode_EncodeFSDefault(path);
     if (bytes == NULL)
         return -2;
-    ret = stat(PyBytes_AS_STRING(bytes), statbuf);
+
+    /* check for embedded null bytes */
+    if (PyBytes_AsStringAndSize(bytes, &cpath, NULL) == -1) {
+        Py_DECREF(bytes);
+        return -2;
+    }
+
+    ret = stat(cpath, statbuf);
     Py_DECREF(bytes);
     return ret;
 #endif
@@ -1080,7 +1091,7 @@ _Py_fopen_obj(PyObject *path, const char *mode)
     FILE *f;
     int async_err = 0;
 #ifdef MS_WINDOWS
-    wchar_t *wpath;
+    const wchar_t *wpath;
     wchar_t wmode[10];
     int usize;
 
@@ -1094,7 +1105,7 @@ _Py_fopen_obj(PyObject *path, const char *mode)
                      Py_TYPE(path));
         return NULL;
     }
-    wpath = PyUnicode_AsUnicode(path);
+    wpath = _PyUnicode_AsUnicode(path);
     if (wpath == NULL)
         return NULL;
 
index a2c2b36..9192bfd 100644 (file)
@@ -312,6 +312,7 @@ parse_internal_render_format_spec(PyObject *format_spec,
                 format->thousands_separators = LT_UNDER_FOUR_LOCALE;
                 break;
             }
+            /* fall through */
         default:
             invalid_comma_type(format->type);
             return 0;
index 616c6eb..ed6b815 100644 (file)
@@ -4,6 +4,7 @@
 #include "Python.h"
 
 #include <ctype.h>
+#include <float.h>
 
 
 #ifdef __cplusplus
@@ -2259,8 +2260,8 @@ skipitem(const char **p_format, va_list *p_va, int flags)
                 /* after 'e', only 's' and 't' is allowed */
                 goto err;
             format++;
-            /* explicit fallthrough to string cases */
         }
+        /* fall through */
 
     case 's': /* string */
     case 'z': /* string or None */
index a23a102..4b3bedf 100644 (file)
@@ -1345,7 +1345,6 @@ resolve_name(PyObject *name, PyObject *globals, int level)
     PyObject *abs_name;
     PyObject *package = NULL;
     PyObject *spec;
-    PyInterpreterState *interp = PyThreadState_GET()->interp;
     Py_ssize_t last_dot;
     PyObject *base;
     int level_up;
@@ -1449,12 +1448,6 @@ resolve_name(PyObject *name, PyObject *globals, int level)
                 "attempted relative import with no known parent package");
         goto error;
     }
-    else if (PyDict_GetItem(interp->modules, package) == NULL) {
-        PyErr_Format(PyExc_SystemError,
-                "Parent module %R not loaded, cannot perform relative "
-                "import", package);
-        goto error;
-    }
 
     for (level_up = 1; level_up < level; level_up += 1) {
         last_dot = PyUnicode_FindChar(package, '.', 0, last_dot, -1);
@@ -1533,18 +1526,7 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
     }
 
     mod = PyDict_GetItem(interp->modules, abs_name);
-    if (mod == Py_None) {
-        PyObject *msg = PyUnicode_FromFormat("import of %R halted; "
-                                             "None in sys.modules", abs_name);
-        if (msg != NULL) {
-            PyErr_SetImportErrorSubclass(PyExc_ModuleNotFoundError, msg,
-                    abs_name, NULL);
-            Py_DECREF(msg);
-        }
-        mod = NULL;
-        goto error;
-    }
-    else if (mod != NULL) {
+    if (mod != NULL && mod != Py_None) {
         _Py_IDENTIFIER(__spec__);
         _Py_IDENTIFIER(_initializing);
         _Py_IDENTIFIER(_lock_unlock_module);
@@ -1571,10 +1553,6 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
             if (initializing == -1)
                 PyErr_Clear();
             if (initializing > 0) {
-#ifdef WITH_THREAD
-                _PyImport_AcquireLock();
-#endif
-                /* _bootstrap._lock_unlock_module() releases the import lock */
                 value = _PyObject_CallMethodIdObjArgs(interp->importlib,
                                                 &PyId__lock_unlock_module, abs_name,
                                                 NULL);
@@ -1585,10 +1563,6 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
         }
     }
     else {
-#ifdef WITH_THREAD
-        _PyImport_AcquireLock();
-#endif
-        /* _bootstrap._find_and_load() releases the import lock */
         mod = _PyObject_CallMethodIdObjArgs(interp->importlib,
                                             &PyId__find_and_load, abs_name,
                                             interp->import_func, NULL);
index 4607c40..18eb1e1 100644 (file)
@@ -14,18 +14,18 @@ const unsigned char _Py_M__importlib[] = {
     90,17,100,30,100,31,132,0,90,18,71,0,100,32,100,33,
     132,0,100,33,131,2,90,19,71,0,100,34,100,35,132,0,
     100,35,131,2,90,20,100,1,100,1,100,36,156,2,100,37,
-    100,38,132,2,90,21,101,22,131,0,90,23,100,92,100,39,
-    100,40,132,1,90,24,100,41,100,42,156,1,100,43,100,44,
-    132,2,90,25,100,45,100,46,132,0,90,26,100,47,100,48,
-    132,0,90,27,100,49,100,50,132,0,90,28,100,51,100,52,
-    132,0,90,29,100,53,100,54,132,0,90,30,100,55,100,56,
-    132,0,90,31,71,0,100,57,100,58,132,0,100,58,131,2,
-    90,32,71,0,100,59,100,60,132,0,100,60,131,2,90,33,
-    71,0,100,61,100,62,132,0,100,62,131,2,90,34,100,63,
-    100,64,132,0,90,35,100,65,100,66,132,0,90,36,100,93,
-    100,67,100,68,132,1,90,37,100,69,100,70,132,0,90,38,
-    100,71,90,39,101,39,100,72,23,0,90,40,100,73,100,74,
-    132,0,90,41,100,75,100,76,132,0,90,42,100,94,100,78,
+    100,38,132,2,90,21,100,92,100,39,100,40,132,1,90,22,
+    100,41,100,42,156,1,100,43,100,44,132,2,90,23,100,45,
+    100,46,132,0,90,24,100,47,100,48,132,0,90,25,100,49,
+    100,50,132,0,90,26,100,51,100,52,132,0,90,27,100,53,
+    100,54,132,0,90,28,100,55,100,56,132,0,90,29,71,0,
+    100,57,100,58,132,0,100,58,131,2,90,30,71,0,100,59,
+    100,60,132,0,100,60,131,2,90,31,71,0,100,61,100,62,
+    132,0,100,62,131,2,90,32,100,63,100,64,132,0,90,33,
+    100,65,100,66,132,0,90,34,100,93,100,67,100,68,132,1,
+    90,35,100,69,100,70,132,0,90,36,100,71,90,37,101,37,
+    100,72,23,0,90,38,100,73,100,74,132,0,90,39,101,40,
+    131,0,90,41,100,75,100,76,132,0,90,42,100,94,100,78,
     100,79,132,1,90,43,100,80,100,81,132,0,90,44,100,82,
     100,83,132,0,90,45,100,1,100,1,102,0,100,77,102,4,
     100,84,100,85,132,1,90,46,100,86,100,87,132,0,90,47,
@@ -86,7 +86,7 @@ const unsigned char _Py_M__importlib[] = {
     69,114,114,111,114,78,41,3,114,1,0,0,0,114,0,0,
     0,0,114,2,0,0,0,114,10,0,0,0,114,10,0,0,
     0,114,10,0,0,0,114,11,0,0,0,114,17,0,0,0,
-    47,0,0,0,115,2,0,0,0,8,1,114,17,0,0,0,
+    48,0,0,0,115,2,0,0,0,8,1,114,17,0,0,0,
     99,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,
     0,64,0,0,0,115,56,0,0,0,101,0,90,1,100,0,
     90,2,100,1,90,3,100,2,100,3,132,0,90,4,100,4,
@@ -115,7 +115,7 @@ const unsigned char _Py_M__importlib[] = {
     101,114,218,5,99,111,117,110,116,218,7,119,97,105,116,101,
     114,115,41,2,218,4,115,101,108,102,114,15,0,0,0,114,
     10,0,0,0,114,10,0,0,0,114,11,0,0,0,218,8,
-    95,95,105,110,105,116,95,95,57,0,0,0,115,12,0,0,
+    95,95,105,110,105,116,95,95,58,0,0,0,115,12,0,0,
     0,0,1,10,1,10,1,6,1,6,1,6,1,122,20,95,
     77,111,100,117,108,101,76,111,99,107,46,95,95,105,110,105,
     116,95,95,99,1,0,0,0,0,0,0,0,4,0,0,0,
@@ -129,7 +129,7 @@ const unsigned char _Py_M__importlib[] = {
     107,105,110,103,95,111,110,218,3,103,101,116,41,4,114,26,
     0,0,0,90,2,109,101,218,3,116,105,100,114,21,0,0,
     0,114,10,0,0,0,114,10,0,0,0,114,11,0,0,0,
-    218,12,104,97,115,95,100,101,97,100,108,111,99,107,65,0,
+    218,12,104,97,115,95,100,101,97,100,108,111,99,107,66,0,
     0,0,115,18,0,0,0,0,2,8,1,6,1,2,1,10,
     1,8,1,4,1,6,1,8,1,122,24,95,77,111,100,117,
     108,101,76,111,99,107,46,104,97,115,95,100,101,97,100,108,
@@ -165,7 +165,7 @@ const unsigned char _Py_M__importlib[] = {
     0,0,114,22,0,0,0,218,7,97,99,113,117,105,114,101,
     114,25,0,0,0,218,7,114,101,108,101,97,115,101,41,2,
     114,26,0,0,0,114,31,0,0,0,114,10,0,0,0,114,
-    10,0,0,0,114,11,0,0,0,114,34,0,0,0,77,0,
+    10,0,0,0,114,11,0,0,0,114,34,0,0,0,78,0,
     0,0,115,32,0,0,0,0,6,8,1,8,1,2,1,2,
     1,8,1,20,1,6,1,14,1,4,1,8,1,12,1,12,
     1,24,2,10,1,18,2,122,19,95,77,111,100,117,108,101,
@@ -188,7 +188,7 @@ const unsigned char _Py_M__importlib[] = {
     111,110,69,114,114,111,114,114,25,0,0,0,114,22,0,0,
     0,114,35,0,0,0,41,2,114,26,0,0,0,114,31,0,
     0,0,114,10,0,0,0,114,10,0,0,0,114,11,0,0,
-    0,114,35,0,0,0,102,0,0,0,115,22,0,0,0,0,
+    0,114,35,0,0,0,103,0,0,0,115,22,0,0,0,0,
     1,8,1,8,1,10,1,8,1,14,1,14,1,10,1,6,
     1,6,1,14,1,122,19,95,77,111,100,117,108,101,76,111,
     99,107,46,114,101,108,101,97,115,101,99,1,0,0,0,0,
@@ -199,13 +199,13 @@ const unsigned char _Py_M__importlib[] = {
     123,125,41,3,218,6,102,111,114,109,97,116,114,15,0,0,
     0,218,2,105,100,41,1,114,26,0,0,0,114,10,0,0,
     0,114,10,0,0,0,114,11,0,0,0,218,8,95,95,114,
-    101,112,114,95,95,115,0,0,0,115,2,0,0,0,0,1,
+    101,112,114,95,95,116,0,0,0,115,2,0,0,0,0,1,
     122,20,95,77,111,100,117,108,101,76,111,99,107,46,95,95,
     114,101,112,114,95,95,78,41,9,114,1,0,0,0,114,0,
     0,0,0,114,2,0,0,0,114,3,0,0,0,114,27,0,
     0,0,114,32,0,0,0,114,34,0,0,0,114,35,0,0,
     0,114,40,0,0,0,114,10,0,0,0,114,10,0,0,0,
-    114,10,0,0,0,114,11,0,0,0,114,18,0,0,0,51,
+    114,10,0,0,0,114,11,0,0,0,114,18,0,0,0,52,
     0,0,0,115,12,0,0,0,8,4,4,2,8,8,8,12,
     8,25,8,13,114,18,0,0,0,99,0,0,0,0,0,0,
     0,0,0,0,0,0,2,0,0,0,64,0,0,0,115,48,
@@ -224,7 +224,7 @@ const unsigned char _Py_M__importlib[] = {
     1,124,0,95,1,100,0,83,0,41,2,78,114,19,0,0,
     0,41,2,114,15,0,0,0,114,24,0,0,0,41,2,114,
     26,0,0,0,114,15,0,0,0,114,10,0,0,0,114,10,
-    0,0,0,114,11,0,0,0,114,27,0,0,0,123,0,0,
+    0,0,0,114,11,0,0,0,114,27,0,0,0,124,0,0,
     0,115,4,0,0,0,0,1,6,1,122,25,95,68,117,109,
     109,121,77,111,100,117,108,101,76,111,99,107,46,95,95,105,
     110,105,116,95,95,99,1,0,0,0,0,0,0,0,1,0,
@@ -232,7 +232,7 @@ const unsigned char _Py_M__importlib[] = {
     0,4,0,106,0,100,1,55,0,2,0,95,0,100,2,83,
     0,41,3,78,114,33,0,0,0,84,41,1,114,24,0,0,
     0,41,1,114,26,0,0,0,114,10,0,0,0,114,10,0,
-    0,0,114,11,0,0,0,114,34,0,0,0,127,0,0,0,
+    0,0,114,11,0,0,0,114,34,0,0,0,128,0,0,0,
     115,4,0,0,0,0,1,14,1,122,24,95,68,117,109,109,
     121,77,111,100,117,108,101,76,111,99,107,46,97,99,113,117,
     105,114,101,99,1,0,0,0,0,0,0,0,1,0,0,0,
@@ -244,7 +244,7 @@ const unsigned char _Py_M__importlib[] = {
     117,105,114,101,100,32,108,111,99,107,114,33,0,0,0,41,
     2,114,24,0,0,0,114,36,0,0,0,41,1,114,26,0,
     0,0,114,10,0,0,0,114,10,0,0,0,114,11,0,0,
-    0,114,35,0,0,0,131,0,0,0,115,6,0,0,0,0,
+    0,114,35,0,0,0,132,0,0,0,115,6,0,0,0,0,
     1,10,1,8,1,122,24,95,68,117,109,109,121,77,111,100,
     117,108,101,76,111,99,107,46,114,101,108,101,97,115,101,99,
     1,0,0,0,0,0,0,0,1,0,0,0,4,0,0,0,
@@ -254,14 +254,14 @@ const unsigned char _Py_M__importlib[] = {
     40,123,33,114,125,41,32,97,116,32,123,125,41,3,114,38,
     0,0,0,114,15,0,0,0,114,39,0,0,0,41,1,114,
     26,0,0,0,114,10,0,0,0,114,10,0,0,0,114,11,
-    0,0,0,114,40,0,0,0,136,0,0,0,115,2,0,0,
+    0,0,0,114,40,0,0,0,137,0,0,0,115,2,0,0,
     0,0,1,122,25,95,68,117,109,109,121,77,111,100,117,108,
     101,76,111,99,107,46,95,95,114,101,112,114,95,95,78,41,
     8,114,1,0,0,0,114,0,0,0,0,114,2,0,0,0,
     114,3,0,0,0,114,27,0,0,0,114,34,0,0,0,114,
     35,0,0,0,114,40,0,0,0,114,10,0,0,0,114,10,
     0,0,0,114,10,0,0,0,114,11,0,0,0,114,41,0,
-    0,0,119,0,0,0,115,10,0,0,0,8,2,4,2,8,
+    0,0,120,0,0,0,115,10,0,0,0,8,2,4,2,8,
     4,8,4,8,5,114,41,0,0,0,99,0,0,0,0,0,
     0,0,0,0,0,0,0,2,0,0,0,64,0,0,0,115,
     36,0,0,0,101,0,90,1,100,0,90,2,100,1,100,2,
@@ -273,1308 +273,1295 @@ const unsigned char _Py_M__importlib[] = {
     124,0,95,1,100,0,83,0,41,1,78,41,2,218,5,95,
     110,97,109,101,218,5,95,108,111,99,107,41,2,114,26,0,
     0,0,114,15,0,0,0,114,10,0,0,0,114,10,0,0,
-    0,114,11,0,0,0,114,27,0,0,0,142,0,0,0,115,
+    0,114,11,0,0,0,114,27,0,0,0,143,0,0,0,115,
     4,0,0,0,0,1,6,1,122,27,95,77,111,100,117,108,
     101,76,111,99,107,77,97,110,97,103,101,114,46,95,95,105,
     110,105,116,95,95,99,1,0,0,0,0,0,0,0,1,0,
-    0,0,10,0,0,0,67,0,0,0,115,42,0,0,0,122,
-    16,116,0,124,0,106,1,131,1,124,0,95,2,87,0,100,
-    0,116,3,106,4,131,0,1,0,88,0,124,0,106,2,106,
-    5,131,0,1,0,100,0,83,0,41,1,78,41,6,218,16,
+    0,0,2,0,0,0,67,0,0,0,115,26,0,0,0,116,
+    0,124,0,106,1,131,1,124,0,95,2,124,0,106,2,106,
+    3,131,0,1,0,100,0,83,0,41,1,78,41,4,218,16,
     95,103,101,116,95,109,111,100,117,108,101,95,108,111,99,107,
-    114,43,0,0,0,114,44,0,0,0,218,4,95,105,109,112,
-    218,12,114,101,108,101,97,115,101,95,108,111,99,107,114,34,
-    0,0,0,41,1,114,26,0,0,0,114,10,0,0,0,114,
-    10,0,0,0,114,11,0,0,0,218,9,95,95,101,110,116,
-    101,114,95,95,146,0,0,0,115,8,0,0,0,0,1,2,
-    1,16,2,10,1,122,28,95,77,111,100,117,108,101,76,111,
-    99,107,77,97,110,97,103,101,114,46,95,95,101,110,116,101,
-    114,95,95,99,1,0,0,0,0,0,0,0,3,0,0,0,
-    1,0,0,0,79,0,0,0,115,14,0,0,0,124,0,106,
-    0,106,1,131,0,1,0,100,0,83,0,41,1,78,41,2,
-    114,44,0,0,0,114,35,0,0,0,41,3,114,26,0,0,
-    0,218,4,97,114,103,115,90,6,107,119,97,114,103,115,114,
-    10,0,0,0,114,10,0,0,0,114,11,0,0,0,218,8,
-    95,95,101,120,105,116,95,95,153,0,0,0,115,2,0,0,
-    0,0,1,122,27,95,77,111,100,117,108,101,76,111,99,107,
-    77,97,110,97,103,101,114,46,95,95,101,120,105,116,95,95,
-    78,41,6,114,1,0,0,0,114,0,0,0,0,114,2,0,
-    0,0,114,27,0,0,0,114,48,0,0,0,114,50,0,0,
-    0,114,10,0,0,0,114,10,0,0,0,114,10,0,0,0,
-    114,11,0,0,0,114,42,0,0,0,140,0,0,0,115,6,
-    0,0,0,8,2,8,4,8,7,114,42,0,0,0,99,1,
-    0,0,0,0,0,0,0,3,0,0,0,11,0,0,0,3,
-    0,0,0,115,106,0,0,0,100,1,125,1,121,14,116,0,
-    136,0,25,0,131,0,125,1,87,0,110,20,4,0,116,1,
-    107,10,114,38,1,0,1,0,1,0,89,0,110,2,88,0,
-    124,1,100,1,107,8,114,102,116,2,100,1,107,8,114,66,
-    116,3,136,0,131,1,125,1,110,8,116,4,136,0,131,1,
-    125,1,135,0,102,1,100,2,100,3,132,8,125,2,116,5,
-    106,6,124,1,124,2,131,2,116,0,136,0,60,0,124,1,
-    83,0,41,4,122,109,71,101,116,32,111,114,32,99,114,101,
+    114,43,0,0,0,114,44,0,0,0,114,34,0,0,0,41,
+    1,114,26,0,0,0,114,10,0,0,0,114,10,0,0,0,
+    114,11,0,0,0,218,9,95,95,101,110,116,101,114,95,95,
+    147,0,0,0,115,4,0,0,0,0,1,12,1,122,28,95,
+    77,111,100,117,108,101,76,111,99,107,77,97,110,97,103,101,
+    114,46,95,95,101,110,116,101,114,95,95,99,1,0,0,0,
+    0,0,0,0,3,0,0,0,1,0,0,0,79,0,0,0,
+    115,14,0,0,0,124,0,106,0,106,1,131,0,1,0,100,
+    0,83,0,41,1,78,41,2,114,44,0,0,0,114,35,0,
+    0,0,41,3,114,26,0,0,0,218,4,97,114,103,115,90,
+    6,107,119,97,114,103,115,114,10,0,0,0,114,10,0,0,
+    0,114,11,0,0,0,218,8,95,95,101,120,105,116,95,95,
+    151,0,0,0,115,2,0,0,0,0,1,122,27,95,77,111,
+    100,117,108,101,76,111,99,107,77,97,110,97,103,101,114,46,
+    95,95,101,120,105,116,95,95,78,41,6,114,1,0,0,0,
+    114,0,0,0,0,114,2,0,0,0,114,27,0,0,0,114,
+    46,0,0,0,114,48,0,0,0,114,10,0,0,0,114,10,
+    0,0,0,114,10,0,0,0,114,11,0,0,0,114,42,0,
+    0,0,141,0,0,0,115,6,0,0,0,8,2,8,4,8,
+    4,114,42,0,0,0,99,1,0,0,0,0,0,0,0,3,
+    0,0,0,17,0,0,0,67,0,0,0,115,130,0,0,0,
+    116,0,106,1,131,0,1,0,122,106,121,14,116,2,124,0,
+    25,0,131,0,125,1,87,0,110,24,4,0,116,3,107,10,
+    114,48,1,0,1,0,1,0,100,1,125,1,89,0,110,2,
+    88,0,124,1,100,1,107,8,114,112,116,4,100,1,107,8,
+    114,76,116,5,124,0,131,1,125,1,110,8,116,6,124,0,
+    131,1,125,1,124,0,102,1,100,2,100,3,132,1,125,2,
+    116,7,106,8,124,1,124,2,131,2,116,2,124,0,60,0,
+    87,0,100,1,116,0,106,9,131,0,1,0,88,0,124,1,
+    83,0,41,4,122,139,71,101,116,32,111,114,32,99,114,101,
     97,116,101,32,116,104,101,32,109,111,100,117,108,101,32,108,
     111,99,107,32,102,111,114,32,97,32,103,105,118,101,110,32,
     109,111,100,117,108,101,32,110,97,109,101,46,10,10,32,32,
-    32,32,83,104,111,117,108,100,32,111,110,108,121,32,98,101,
-    32,99,97,108,108,101,100,32,119,105,116,104,32,116,104,101,
-    32,105,109,112,111,114,116,32,108,111,99,107,32,116,97,107,
-    101,110,46,78,99,1,0,0,0,0,0,0,0,1,0,0,
-    0,2,0,0,0,19,0,0,0,115,10,0,0,0,116,0,
-    136,0,61,0,100,0,83,0,41,1,78,41,1,218,13,95,
-    109,111,100,117,108,101,95,108,111,99,107,115,41,1,218,1,
-    95,41,1,114,15,0,0,0,114,10,0,0,0,114,11,0,
-    0,0,218,2,99,98,173,0,0,0,115,2,0,0,0,0,
-    1,122,28,95,103,101,116,95,109,111,100,117,108,101,95,108,
-    111,99,107,46,60,108,111,99,97,108,115,62,46,99,98,41,
-    7,114,51,0,0,0,218,8,75,101,121,69,114,114,111,114,
-    114,20,0,0,0,114,41,0,0,0,114,18,0,0,0,218,
-    8,95,119,101,97,107,114,101,102,90,3,114,101,102,41,3,
-    114,15,0,0,0,114,21,0,0,0,114,53,0,0,0,114,
-    10,0,0,0,41,1,114,15,0,0,0,114,11,0,0,0,
-    114,45,0,0,0,159,0,0,0,115,24,0,0,0,0,4,
-    4,1,2,1,14,1,14,1,6,1,8,1,8,1,10,2,
-    8,1,12,2,16,1,114,45,0,0,0,99,1,0,0,0,
-    0,0,0,0,2,0,0,0,11,0,0,0,67,0,0,0,
-    115,62,0,0,0,116,0,124,0,131,1,125,1,116,1,106,
-    2,131,0,1,0,121,12,124,1,106,3,131,0,1,0,87,
-    0,110,20,4,0,116,4,107,10,114,48,1,0,1,0,1,
-    0,89,0,110,10,88,0,124,1,106,5,131,0,1,0,100,
-    1,83,0,41,2,97,21,1,0,0,82,101,108,101,97,115,
-    101,32,116,104,101,32,103,108,111,98,97,108,32,105,109,112,
-    111,114,116,32,108,111,99,107,44,32,97,110,100,32,97,99,
-    113,117,105,114,101,115,32,116,104,101,110,32,114,101,108,101,
-    97,115,101,32,116,104,101,10,32,32,32,32,109,111,100,117,
-    108,101,32,108,111,99,107,32,102,111,114,32,97,32,103,105,
-    118,101,110,32,109,111,100,117,108,101,32,110,97,109,101,46,
-    10,32,32,32,32,84,104,105,115,32,105,115,32,117,115,101,
-    100,32,116,111,32,101,110,115,117,114,101,32,97,32,109,111,
-    100,117,108,101,32,105,115,32,99,111,109,112,108,101,116,101,
-    108,121,32,105,110,105,116,105,97,108,105,122,101,100,44,32,
-    105,110,32,116,104,101,10,32,32,32,32,101,118,101,110,116,
-    32,105,116,32,105,115,32,98,101,105,110,103,32,105,109,112,
-    111,114,116,101,100,32,98,121,32,97,110,111,116,104,101,114,
-    32,116,104,114,101,97,100,46,10,10,32,32,32,32,83,104,
-    111,117,108,100,32,111,110,108,121,32,98,101,32,99,97,108,
-    108,101,100,32,119,105,116,104,32,116,104,101,32,105,109,112,
-    111,114,116,32,108,111,99,107,32,116,97,107,101,110,46,78,
-    41,6,114,45,0,0,0,114,46,0,0,0,114,47,0,0,
-    0,114,34,0,0,0,114,17,0,0,0,114,35,0,0,0,
-    41,2,114,15,0,0,0,114,21,0,0,0,114,10,0,0,
-    0,114,10,0,0,0,114,11,0,0,0,218,19,95,108,111,
-    99,107,95,117,110,108,111,99,107,95,109,111,100,117,108,101,
-    178,0,0,0,115,14,0,0,0,0,7,8,1,8,1,2,
-    1,12,1,14,3,6,2,114,56,0,0,0,99,1,0,0,
-    0,0,0,0,0,3,0,0,0,3,0,0,0,79,0,0,
-    0,115,10,0,0,0,124,0,124,1,124,2,142,1,83,0,
-    41,1,97,46,1,0,0,114,101,109,111,118,101,95,105,109,
-    112,111,114,116,108,105,98,95,102,114,97,109,101,115,32,105,
-    110,32,105,109,112,111,114,116,46,99,32,119,105,108,108,32,
-    97,108,119,97,121,115,32,114,101,109,111,118,101,32,115,101,
-    113,117,101,110,99,101,115,10,32,32,32,32,111,102,32,105,
-    109,112,111,114,116,108,105,98,32,102,114,97,109,101,115,32,
-    116,104,97,116,32,101,110,100,32,119,105,116,104,32,97,32,
-    99,97,108,108,32,116,111,32,116,104,105,115,32,102,117,110,
-    99,116,105,111,110,10,10,32,32,32,32,85,115,101,32,105,
-    116,32,105,110,115,116,101,97,100,32,111,102,32,97,32,110,
-    111,114,109,97,108,32,99,97,108,108,32,105,110,32,112,108,
-    97,99,101,115,32,119,104,101,114,101,32,105,110,99,108,117,
-    100,105,110,103,32,116,104,101,32,105,109,112,111,114,116,108,
-    105,98,10,32,32,32,32,102,114,97,109,101,115,32,105,110,
-    116,114,111,100,117,99,101,115,32,117,110,119,97,110,116,101,
-    100,32,110,111,105,115,101,32,105,110,116,111,32,116,104,101,
-    32,116,114,97,99,101,98,97,99,107,32,40,101,46,103,46,
-    32,119,104,101,110,32,101,120,101,99,117,116,105,110,103,10,
-    32,32,32,32,109,111,100,117,108,101,32,99,111,100,101,41,
-    10,32,32,32,32,114,10,0,0,0,41,3,218,1,102,114,
-    49,0,0,0,90,4,107,119,100,115,114,10,0,0,0,114,
-    10,0,0,0,114,11,0,0,0,218,25,95,99,97,108,108,
-    95,119,105,116,104,95,102,114,97,109,101,115,95,114,101,109,
-    111,118,101,100,197,0,0,0,115,2,0,0,0,0,8,114,
-    58,0,0,0,114,33,0,0,0,41,1,218,9,118,101,114,
-    98,111,115,105,116,121,99,1,0,0,0,1,0,0,0,3,
-    0,0,0,4,0,0,0,71,0,0,0,115,54,0,0,0,
-    116,0,106,1,106,2,124,1,107,5,114,50,124,0,106,3,
-    100,6,131,1,115,30,100,3,124,0,23,0,125,0,116,4,
-    124,0,106,5,124,2,142,0,116,0,106,6,100,4,141,2,
-    1,0,100,5,83,0,41,7,122,61,80,114,105,110,116,32,
-    116,104,101,32,109,101,115,115,97,103,101,32,116,111,32,115,
-    116,100,101,114,114,32,105,102,32,45,118,47,80,89,84,72,
-    79,78,86,69,82,66,79,83,69,32,105,115,32,116,117,114,
-    110,101,100,32,111,110,46,250,1,35,250,7,105,109,112,111,
-    114,116,32,122,2,35,32,41,1,90,4,102,105,108,101,78,
-    41,2,114,60,0,0,0,114,61,0,0,0,41,7,114,14,
-    0,0,0,218,5,102,108,97,103,115,218,7,118,101,114,98,
-    111,115,101,218,10,115,116,97,114,116,115,119,105,116,104,218,
-    5,112,114,105,110,116,114,38,0,0,0,218,6,115,116,100,
-    101,114,114,41,3,218,7,109,101,115,115,97,103,101,114,59,
-    0,0,0,114,49,0,0,0,114,10,0,0,0,114,10,0,
-    0,0,114,11,0,0,0,218,16,95,118,101,114,98,111,115,
-    101,95,109,101,115,115,97,103,101,208,0,0,0,115,8,0,
-    0,0,0,2,12,1,10,1,8,1,114,68,0,0,0,99,
-    1,0,0,0,0,0,0,0,2,0,0,0,3,0,0,0,
-    3,0,0,0,115,26,0,0,0,135,0,102,1,100,1,100,
-    2,132,8,125,1,116,0,124,1,136,0,131,2,1,0,124,
-    1,83,0,41,3,122,49,68,101,99,111,114,97,116,111,114,
-    32,116,111,32,118,101,114,105,102,121,32,116,104,101,32,110,
-    97,109,101,100,32,109,111,100,117,108,101,32,105,115,32,98,
-    117,105,108,116,45,105,110,46,99,2,0,0,0,0,0,0,
-    0,2,0,0,0,4,0,0,0,19,0,0,0,115,38,0,
-    0,0,124,1,116,0,106,1,107,7,114,28,116,2,100,1,
-    106,3,124,1,131,1,124,1,100,2,141,2,130,1,136,0,
-    124,0,124,1,131,2,83,0,41,3,78,122,29,123,33,114,
-    125,32,105,115,32,110,111,116,32,97,32,98,117,105,108,116,
-    45,105,110,32,109,111,100,117,108,101,41,1,114,15,0,0,
-    0,41,4,114,14,0,0,0,218,20,98,117,105,108,116,105,
-    110,95,109,111,100,117,108,101,95,110,97,109,101,115,218,11,
-    73,109,112,111,114,116,69,114,114,111,114,114,38,0,0,0,
-    41,2,114,26,0,0,0,218,8,102,117,108,108,110,97,109,
-    101,41,1,218,3,102,120,110,114,10,0,0,0,114,11,0,
-    0,0,218,25,95,114,101,113,117,105,114,101,115,95,98,117,
-    105,108,116,105,110,95,119,114,97,112,112,101,114,218,0,0,
-    0,115,8,0,0,0,0,1,10,1,10,1,8,1,122,52,
-    95,114,101,113,117,105,114,101,115,95,98,117,105,108,116,105,
-    110,46,60,108,111,99,97,108,115,62,46,95,114,101,113,117,
-    105,114,101,115,95,98,117,105,108,116,105,110,95,119,114,97,
-    112,112,101,114,41,1,114,12,0,0,0,41,2,114,72,0,
-    0,0,114,73,0,0,0,114,10,0,0,0,41,1,114,72,
-    0,0,0,114,11,0,0,0,218,17,95,114,101,113,117,105,
-    114,101,115,95,98,117,105,108,116,105,110,216,0,0,0,115,
-    6,0,0,0,0,2,12,5,10,1,114,74,0,0,0,99,
+    32,32,65,99,113,117,105,114,101,47,114,101,108,101,97,115,
+    101,32,105,110,116,101,114,110,97,108,108,121,32,116,104,101,
+    32,103,108,111,98,97,108,32,105,109,112,111,114,116,32,108,
+    111,99,107,32,116,111,32,112,114,111,116,101,99,116,10,32,
+    32,32,32,95,109,111,100,117,108,101,95,108,111,99,107,115,
+    46,78,99,2,0,0,0,0,0,0,0,2,0,0,0,10,
+    0,0,0,83,0,0,0,115,48,0,0,0,116,0,106,1,
+    131,0,1,0,122,24,116,2,106,3,124,1,131,1,124,0,
+    107,8,114,30,116,2,124,1,61,0,87,0,100,0,116,0,
+    106,4,131,0,1,0,88,0,100,0,83,0,41,1,78,41,
+    5,218,4,95,105,109,112,218,12,97,99,113,117,105,114,101,
+    95,108,111,99,107,218,13,95,109,111,100,117,108,101,95,108,
+    111,99,107,115,114,30,0,0,0,218,12,114,101,108,101,97,
+    115,101,95,108,111,99,107,41,2,218,3,114,101,102,114,15,
+    0,0,0,114,10,0,0,0,114,10,0,0,0,114,11,0,
+    0,0,218,2,99,98,176,0,0,0,115,10,0,0,0,0,
+    1,8,1,2,4,14,1,10,2,122,28,95,103,101,116,95,
+    109,111,100,117,108,101,95,108,111,99,107,46,60,108,111,99,
+    97,108,115,62,46,99,98,41,10,114,49,0,0,0,114,50,
+    0,0,0,114,51,0,0,0,218,8,75,101,121,69,114,114,
+    111,114,114,20,0,0,0,114,41,0,0,0,114,18,0,0,
+    0,218,8,95,119,101,97,107,114,101,102,114,53,0,0,0,
+    114,52,0,0,0,41,3,114,15,0,0,0,114,21,0,0,
+    0,114,54,0,0,0,114,10,0,0,0,114,10,0,0,0,
+    114,11,0,0,0,114,45,0,0,0,157,0,0,0,115,28,
+    0,0,0,0,6,8,1,2,1,2,1,14,1,14,1,10,
+    2,8,1,8,1,10,2,8,2,12,11,20,2,10,2,114,
+    45,0,0,0,99,1,0,0,0,0,0,0,0,2,0,0,
+    0,11,0,0,0,67,0,0,0,115,54,0,0,0,116,0,
+    124,0,131,1,125,1,121,12,124,1,106,1,131,0,1,0,
+    87,0,110,20,4,0,116,2,107,10,114,40,1,0,1,0,
+    1,0,89,0,110,10,88,0,124,1,106,3,131,0,1,0,
+    100,1,83,0,41,2,122,189,65,99,113,117,105,114,101,115,
+    32,116,104,101,110,32,114,101,108,101,97,115,101,115,32,116,
+    104,101,32,109,111,100,117,108,101,32,108,111,99,107,32,102,
+    111,114,32,97,32,103,105,118,101,110,32,109,111,100,117,108,
+    101,32,110,97,109,101,46,10,10,32,32,32,32,84,104,105,
+    115,32,105,115,32,117,115,101,100,32,116,111,32,101,110,115,
+    117,114,101,32,97,32,109,111,100,117,108,101,32,105,115,32,
+    99,111,109,112,108,101,116,101,108,121,32,105,110,105,116,105,
+    97,108,105,122,101,100,44,32,105,110,32,116,104,101,10,32,
+    32,32,32,101,118,101,110,116,32,105,116,32,105,115,32,98,
+    101,105,110,103,32,105,109,112,111,114,116,101,100,32,98,121,
+    32,97,110,111,116,104,101,114,32,116,104,114,101,97,100,46,
+    10,32,32,32,32,78,41,4,114,45,0,0,0,114,34,0,
+    0,0,114,17,0,0,0,114,35,0,0,0,41,2,114,15,
+    0,0,0,114,21,0,0,0,114,10,0,0,0,114,10,0,
+    0,0,114,11,0,0,0,218,19,95,108,111,99,107,95,117,
+    110,108,111,99,107,95,109,111,100,117,108,101,194,0,0,0,
+    115,12,0,0,0,0,6,8,1,2,1,12,1,14,3,6,
+    2,114,57,0,0,0,99,1,0,0,0,0,0,0,0,3,
+    0,0,0,3,0,0,0,79,0,0,0,115,10,0,0,0,
+    124,0,124,1,124,2,142,1,83,0,41,1,97,46,1,0,
+    0,114,101,109,111,118,101,95,105,109,112,111,114,116,108,105,
+    98,95,102,114,97,109,101,115,32,105,110,32,105,109,112,111,
+    114,116,46,99,32,119,105,108,108,32,97,108,119,97,121,115,
+    32,114,101,109,111,118,101,32,115,101,113,117,101,110,99,101,
+    115,10,32,32,32,32,111,102,32,105,109,112,111,114,116,108,
+    105,98,32,102,114,97,109,101,115,32,116,104,97,116,32,101,
+    110,100,32,119,105,116,104,32,97,32,99,97,108,108,32,116,
+    111,32,116,104,105,115,32,102,117,110,99,116,105,111,110,10,
+    10,32,32,32,32,85,115,101,32,105,116,32,105,110,115,116,
+    101,97,100,32,111,102,32,97,32,110,111,114,109,97,108,32,
+    99,97,108,108,32,105,110,32,112,108,97,99,101,115,32,119,
+    104,101,114,101,32,105,110,99,108,117,100,105,110,103,32,116,
+    104,101,32,105,109,112,111,114,116,108,105,98,10,32,32,32,
+    32,102,114,97,109,101,115,32,105,110,116,114,111,100,117,99,
+    101,115,32,117,110,119,97,110,116,101,100,32,110,111,105,115,
+    101,32,105,110,116,111,32,116,104,101,32,116,114,97,99,101,
+    98,97,99,107,32,40,101,46,103,46,32,119,104,101,110,32,
+    101,120,101,99,117,116,105,110,103,10,32,32,32,32,109,111,
+    100,117,108,101,32,99,111,100,101,41,10,32,32,32,32,114,
+    10,0,0,0,41,3,218,1,102,114,47,0,0,0,90,4,
+    107,119,100,115,114,10,0,0,0,114,10,0,0,0,114,11,
+    0,0,0,218,25,95,99,97,108,108,95,119,105,116,104,95,
+    102,114,97,109,101,115,95,114,101,109,111,118,101,100,211,0,
+    0,0,115,2,0,0,0,0,8,114,59,0,0,0,114,33,
+    0,0,0,41,1,218,9,118,101,114,98,111,115,105,116,121,
+    99,1,0,0,0,1,0,0,0,3,0,0,0,4,0,0,
+    0,71,0,0,0,115,54,0,0,0,116,0,106,1,106,2,
+    124,1,107,5,114,50,124,0,106,3,100,6,131,1,115,30,
+    100,3,124,0,23,0,125,0,116,4,124,0,106,5,124,2,
+    142,0,116,0,106,6,100,4,141,2,1,0,100,5,83,0,
+    41,7,122,61,80,114,105,110,116,32,116,104,101,32,109,101,
+    115,115,97,103,101,32,116,111,32,115,116,100,101,114,114,32,
+    105,102,32,45,118,47,80,89,84,72,79,78,86,69,82,66,
+    79,83,69,32,105,115,32,116,117,114,110,101,100,32,111,110,
+    46,250,1,35,250,7,105,109,112,111,114,116,32,122,2,35,
+    32,41,1,90,4,102,105,108,101,78,41,2,114,61,0,0,
+    0,114,62,0,0,0,41,7,114,14,0,0,0,218,5,102,
+    108,97,103,115,218,7,118,101,114,98,111,115,101,218,10,115,
+    116,97,114,116,115,119,105,116,104,218,5,112,114,105,110,116,
+    114,38,0,0,0,218,6,115,116,100,101,114,114,41,3,218,
+    7,109,101,115,115,97,103,101,114,60,0,0,0,114,47,0,
+    0,0,114,10,0,0,0,114,10,0,0,0,114,11,0,0,
+    0,218,16,95,118,101,114,98,111,115,101,95,109,101,115,115,
+    97,103,101,222,0,0,0,115,8,0,0,0,0,2,12,1,
+    10,1,8,1,114,69,0,0,0,99,1,0,0,0,0,0,
+    0,0,2,0,0,0,3,0,0,0,3,0,0,0,115,26,
+    0,0,0,135,0,102,1,100,1,100,2,132,8,125,1,116,
+    0,124,1,136,0,131,2,1,0,124,1,83,0,41,3,122,
+    49,68,101,99,111,114,97,116,111,114,32,116,111,32,118,101,
+    114,105,102,121,32,116,104,101,32,110,97,109,101,100,32,109,
+    111,100,117,108,101,32,105,115,32,98,117,105,108,116,45,105,
+    110,46,99,2,0,0,0,0,0,0,0,2,0,0,0,4,
+    0,0,0,19,0,0,0,115,38,0,0,0,124,1,116,0,
+    106,1,107,7,114,28,116,2,100,1,106,3,124,1,131,1,
+    124,1,100,2,141,2,130,1,136,0,124,0,124,1,131,2,
+    83,0,41,3,78,122,29,123,33,114,125,32,105,115,32,110,
+    111,116,32,97,32,98,117,105,108,116,45,105,110,32,109,111,
+    100,117,108,101,41,1,114,15,0,0,0,41,4,114,14,0,
+    0,0,218,20,98,117,105,108,116,105,110,95,109,111,100,117,
+    108,101,95,110,97,109,101,115,218,11,73,109,112,111,114,116,
+    69,114,114,111,114,114,38,0,0,0,41,2,114,26,0,0,
+    0,218,8,102,117,108,108,110,97,109,101,41,1,218,3,102,
+    120,110,114,10,0,0,0,114,11,0,0,0,218,25,95,114,
+    101,113,117,105,114,101,115,95,98,117,105,108,116,105,110,95,
+    119,114,97,112,112,101,114,232,0,0,0,115,8,0,0,0,
+    0,1,10,1,10,1,8,1,122,52,95,114,101,113,117,105,
+    114,101,115,95,98,117,105,108,116,105,110,46,60,108,111,99,
+    97,108,115,62,46,95,114,101,113,117,105,114,101,115,95,98,
+    117,105,108,116,105,110,95,119,114,97,112,112,101,114,41,1,
+    114,12,0,0,0,41,2,114,73,0,0,0,114,74,0,0,
+    0,114,10,0,0,0,41,1,114,73,0,0,0,114,11,0,
+    0,0,218,17,95,114,101,113,117,105,114,101,115,95,98,117,
+    105,108,116,105,110,230,0,0,0,115,6,0,0,0,0,2,
+    12,5,10,1,114,75,0,0,0,99,1,0,0,0,0,0,
+    0,0,2,0,0,0,3,0,0,0,3,0,0,0,115,26,
+    0,0,0,135,0,102,1,100,1,100,2,132,8,125,1,116,
+    0,124,1,136,0,131,2,1,0,124,1,83,0,41,3,122,
+    47,68,101,99,111,114,97,116,111,114,32,116,111,32,118,101,
+    114,105,102,121,32,116,104,101,32,110,97,109,101,100,32,109,
+    111,100,117,108,101,32,105,115,32,102,114,111,122,101,110,46,
+    99,2,0,0,0,0,0,0,0,2,0,0,0,4,0,0,
+    0,19,0,0,0,115,38,0,0,0,116,0,106,1,124,1,
+    131,1,115,28,116,2,100,1,106,3,124,1,131,1,124,1,
+    100,2,141,2,130,1,136,0,124,0,124,1,131,2,83,0,
+    41,3,78,122,27,123,33,114,125,32,105,115,32,110,111,116,
+    32,97,32,102,114,111,122,101,110,32,109,111,100,117,108,101,
+    41,1,114,15,0,0,0,41,4,114,49,0,0,0,218,9,
+    105,115,95,102,114,111,122,101,110,114,71,0,0,0,114,38,
+    0,0,0,41,2,114,26,0,0,0,114,72,0,0,0,41,
+    1,114,73,0,0,0,114,10,0,0,0,114,11,0,0,0,
+    218,24,95,114,101,113,117,105,114,101,115,95,102,114,111,122,
+    101,110,95,119,114,97,112,112,101,114,243,0,0,0,115,8,
+    0,0,0,0,1,10,1,10,1,8,1,122,50,95,114,101,
+    113,117,105,114,101,115,95,102,114,111,122,101,110,46,60,108,
+    111,99,97,108,115,62,46,95,114,101,113,117,105,114,101,115,
+    95,102,114,111,122,101,110,95,119,114,97,112,112,101,114,41,
+    1,114,12,0,0,0,41,2,114,73,0,0,0,114,77,0,
+    0,0,114,10,0,0,0,41,1,114,73,0,0,0,114,11,
+    0,0,0,218,16,95,114,101,113,117,105,114,101,115,95,102,
+    114,111,122,101,110,241,0,0,0,115,6,0,0,0,0,2,
+    12,5,10,1,114,78,0,0,0,99,2,0,0,0,0,0,
+    0,0,4,0,0,0,3,0,0,0,67,0,0,0,115,62,
+    0,0,0,116,0,124,1,124,0,131,2,125,2,124,1,116,
+    1,106,2,107,6,114,50,116,1,106,2,124,1,25,0,125,
+    3,116,3,124,2,124,3,131,2,1,0,116,1,106,2,124,
+    1,25,0,83,0,116,4,124,2,131,1,83,0,100,1,83,
+    0,41,2,122,128,76,111,97,100,32,116,104,101,32,115,112,
+    101,99,105,102,105,101,100,32,109,111,100,117,108,101,32,105,
+    110,116,111,32,115,121,115,46,109,111,100,117,108,101,115,32,
+    97,110,100,32,114,101,116,117,114,110,32,105,116,46,10,10,
+    32,32,32,32,84,104,105,115,32,109,101,116,104,111,100,32,
+    105,115,32,100,101,112,114,101,99,97,116,101,100,46,32,32,
+    85,115,101,32,108,111,97,100,101,114,46,101,120,101,99,95,
+    109,111,100,117,108,101,32,105,110,115,116,101,97,100,46,10,
+    10,32,32,32,32,78,41,5,218,16,115,112,101,99,95,102,
+    114,111,109,95,108,111,97,100,101,114,114,14,0,0,0,218,
+    7,109,111,100,117,108,101,115,218,5,95,101,120,101,99,218,
+    5,95,108,111,97,100,41,4,114,26,0,0,0,114,72,0,
+    0,0,218,4,115,112,101,99,218,6,109,111,100,117,108,101,
+    114,10,0,0,0,114,10,0,0,0,114,11,0,0,0,218,
+    17,95,108,111,97,100,95,109,111,100,117,108,101,95,115,104,
+    105,109,253,0,0,0,115,12,0,0,0,0,6,10,1,10,
+    1,10,1,10,1,10,2,114,85,0,0,0,99,1,0,0,
+    0,0,0,0,0,5,0,0,0,35,0,0,0,67,0,0,
+    0,115,216,0,0,0,116,0,124,0,100,1,100,0,131,3,
+    125,1,116,1,124,1,100,2,131,2,114,54,121,10,124,1,
+    106,2,124,0,131,1,83,0,4,0,116,3,107,10,114,52,
+    1,0,1,0,1,0,89,0,110,2,88,0,121,10,124,0,
+    106,4,125,2,87,0,110,20,4,0,116,5,107,10,114,84,
+    1,0,1,0,1,0,89,0,110,18,88,0,124,2,100,0,
+    107,9,114,102,116,6,124,2,131,1,83,0,121,10,124,0,
+    106,7,125,3,87,0,110,24,4,0,116,5,107,10,114,136,
+    1,0,1,0,1,0,100,3,125,3,89,0,110,2,88,0,
+    121,10,124,0,106,8,125,4,87,0,110,50,4,0,116,5,
+    107,10,114,198,1,0,1,0,1,0,124,1,100,0,107,8,
+    114,182,100,4,106,9,124,3,131,1,83,0,100,5,106,9,
+    124,3,124,1,131,2,83,0,89,0,110,14,88,0,100,6,
+    106,9,124,3,124,4,131,2,83,0,100,0,83,0,41,7,
+    78,218,10,95,95,108,111,97,100,101,114,95,95,218,11,109,
+    111,100,117,108,101,95,114,101,112,114,250,1,63,122,13,60,
+    109,111,100,117,108,101,32,123,33,114,125,62,122,20,60,109,
+    111,100,117,108,101,32,123,33,114,125,32,40,123,33,114,125,
+    41,62,122,23,60,109,111,100,117,108,101,32,123,33,114,125,
+    32,102,114,111,109,32,123,33,114,125,62,41,10,114,6,0,
+    0,0,114,4,0,0,0,114,87,0,0,0,218,9,69,120,
+    99,101,112,116,105,111,110,218,8,95,95,115,112,101,99,95,
+    95,218,14,65,116,116,114,105,98,117,116,101,69,114,114,111,
+    114,218,22,95,109,111,100,117,108,101,95,114,101,112,114,95,
+    102,114,111,109,95,115,112,101,99,114,1,0,0,0,218,8,
+    95,95,102,105,108,101,95,95,114,38,0,0,0,41,5,114,
+    84,0,0,0,218,6,108,111,97,100,101,114,114,83,0,0,
+    0,114,15,0,0,0,218,8,102,105,108,101,110,97,109,101,
+    114,10,0,0,0,114,10,0,0,0,114,11,0,0,0,218,
+    12,95,109,111,100,117,108,101,95,114,101,112,114,13,1,0,
+    0,115,46,0,0,0,0,2,12,1,10,4,2,1,10,1,
+    14,1,6,1,2,1,10,1,14,1,6,2,8,1,8,4,
+    2,1,10,1,14,1,10,1,2,1,10,1,14,1,8,1,
+    10,2,18,2,114,96,0,0,0,99,0,0,0,0,0,0,
+    0,0,0,0,0,0,2,0,0,0,64,0,0,0,115,36,
+    0,0,0,101,0,90,1,100,0,90,2,100,1,100,2,132,
+    0,90,3,100,3,100,4,132,0,90,4,100,5,100,6,132,
+    0,90,5,100,7,83,0,41,8,218,17,95,105,110,115,116,
+    97,108,108,101,100,95,115,97,102,101,108,121,99,2,0,0,
+    0,0,0,0,0,2,0,0,0,2,0,0,0,67,0,0,
+    0,115,18,0,0,0,124,1,124,0,95,0,124,1,106,1,
+    124,0,95,2,100,0,83,0,41,1,78,41,3,218,7,95,
+    109,111,100,117,108,101,114,90,0,0,0,218,5,95,115,112,
+    101,99,41,2,114,26,0,0,0,114,84,0,0,0,114,10,
+    0,0,0,114,10,0,0,0,114,11,0,0,0,114,27,0,
+    0,0,51,1,0,0,115,4,0,0,0,0,1,6,1,122,
+    26,95,105,110,115,116,97,108,108,101,100,95,115,97,102,101,
+    108,121,46,95,95,105,110,105,116,95,95,99,1,0,0,0,
+    0,0,0,0,1,0,0,0,3,0,0,0,67,0,0,0,
+    115,28,0,0,0,100,1,124,0,106,0,95,1,124,0,106,
+    2,116,3,106,4,124,0,106,0,106,5,60,0,100,0,83,
+    0,41,2,78,84,41,6,114,99,0,0,0,218,13,95,105,
+    110,105,116,105,97,108,105,122,105,110,103,114,98,0,0,0,
+    114,14,0,0,0,114,80,0,0,0,114,15,0,0,0,41,
+    1,114,26,0,0,0,114,10,0,0,0,114,10,0,0,0,
+    114,11,0,0,0,114,46,0,0,0,55,1,0,0,115,4,
+    0,0,0,0,4,8,1,122,27,95,105,110,115,116,97,108,
+    108,101,100,95,115,97,102,101,108,121,46,95,95,101,110,116,
+    101,114,95,95,99,1,0,0,0,0,0,0,0,3,0,0,
+    0,17,0,0,0,71,0,0,0,115,98,0,0,0,122,82,
+    124,0,106,0,125,2,116,1,100,1,100,2,132,0,124,1,
+    68,0,131,1,131,1,114,64,121,14,116,2,106,3,124,2,
+    106,4,61,0,87,0,113,80,4,0,116,5,107,10,114,60,
+    1,0,1,0,1,0,89,0,113,80,88,0,110,16,116,6,
+    100,3,124,2,106,4,124,2,106,7,131,3,1,0,87,0,
+    100,0,100,4,124,0,106,0,95,8,88,0,100,0,83,0,
+    41,5,78,99,1,0,0,0,0,0,0,0,2,0,0,0,
+    3,0,0,0,115,0,0,0,115,22,0,0,0,124,0,93,
+    14,125,1,124,1,100,0,107,9,86,0,1,0,113,2,100,
+    0,83,0,41,1,78,114,10,0,0,0,41,2,90,2,46,
+    48,90,3,97,114,103,114,10,0,0,0,114,10,0,0,0,
+    114,11,0,0,0,250,9,60,103,101,110,101,120,112,114,62,
+    65,1,0,0,115,2,0,0,0,4,0,122,45,95,105,110,
+    115,116,97,108,108,101,100,95,115,97,102,101,108,121,46,95,
+    95,101,120,105,116,95,95,46,60,108,111,99,97,108,115,62,
+    46,60,103,101,110,101,120,112,114,62,122,18,105,109,112,111,
+    114,116,32,123,33,114,125,32,35,32,123,33,114,125,70,41,
+    9,114,99,0,0,0,218,3,97,110,121,114,14,0,0,0,
+    114,80,0,0,0,114,15,0,0,0,114,55,0,0,0,114,
+    69,0,0,0,114,94,0,0,0,114,100,0,0,0,41,3,
+    114,26,0,0,0,114,47,0,0,0,114,83,0,0,0,114,
+    10,0,0,0,114,10,0,0,0,114,11,0,0,0,114,48,
+    0,0,0,62,1,0,0,115,18,0,0,0,0,1,2,1,
+    6,1,18,1,2,1,14,1,14,1,8,2,20,2,122,26,
+    95,105,110,115,116,97,108,108,101,100,95,115,97,102,101,108,
+    121,46,95,95,101,120,105,116,95,95,78,41,6,114,1,0,
+    0,0,114,0,0,0,0,114,2,0,0,0,114,27,0,0,
+    0,114,46,0,0,0,114,48,0,0,0,114,10,0,0,0,
+    114,10,0,0,0,114,10,0,0,0,114,11,0,0,0,114,
+    97,0,0,0,49,1,0,0,115,6,0,0,0,8,2,8,
+    4,8,7,114,97,0,0,0,99,0,0,0,0,0,0,0,
+    0,0,0,0,0,4,0,0,0,64,0,0,0,115,114,0,
+    0,0,101,0,90,1,100,0,90,2,100,1,90,3,100,2,
+    100,2,100,2,100,3,156,3,100,4,100,5,132,2,90,4,
+    100,6,100,7,132,0,90,5,100,8,100,9,132,0,90,6,
+    101,7,100,10,100,11,132,0,131,1,90,8,101,8,106,9,
+    100,12,100,11,132,0,131,1,90,8,101,7,100,13,100,14,
+    132,0,131,1,90,10,101,7,100,15,100,16,132,0,131,1,
+    90,11,101,11,106,9,100,17,100,16,132,0,131,1,90,11,
+    100,2,83,0,41,18,218,10,77,111,100,117,108,101,83,112,
+    101,99,97,208,5,0,0,84,104,101,32,115,112,101,99,105,
+    102,105,99,97,116,105,111,110,32,102,111,114,32,97,32,109,
+    111,100,117,108,101,44,32,117,115,101,100,32,102,111,114,32,
+    108,111,97,100,105,110,103,46,10,10,32,32,32,32,65,32,
+    109,111,100,117,108,101,39,115,32,115,112,101,99,32,105,115,
+    32,116,104,101,32,115,111,117,114,99,101,32,102,111,114,32,
+    105,110,102,111,114,109,97,116,105,111,110,32,97,98,111,117,
+    116,32,116,104,101,32,109,111,100,117,108,101,46,32,32,70,
+    111,114,10,32,32,32,32,100,97,116,97,32,97,115,115,111,
+    99,105,97,116,101,100,32,119,105,116,104,32,116,104,101,32,
+    109,111,100,117,108,101,44,32,105,110,99,108,117,100,105,110,
+    103,32,115,111,117,114,99,101,44,32,117,115,101,32,116,104,
+    101,32,115,112,101,99,39,115,10,32,32,32,32,108,111,97,
+    100,101,114,46,10,10,32,32,32,32,96,110,97,109,101,96,
+    32,105,115,32,116,104,101,32,97,98,115,111,108,117,116,101,
+    32,110,97,109,101,32,111,102,32,116,104,101,32,109,111,100,
+    117,108,101,46,32,32,96,108,111,97,100,101,114,96,32,105,
+    115,32,116,104,101,32,108,111,97,100,101,114,10,32,32,32,
+    32,116,111,32,117,115,101,32,119,104,101,110,32,108,111,97,
+    100,105,110,103,32,116,104,101,32,109,111,100,117,108,101,46,
+    32,32,96,112,97,114,101,110,116,96,32,105,115,32,116,104,
+    101,32,110,97,109,101,32,111,102,32,116,104,101,10,32,32,
+    32,32,112,97,99,107,97,103,101,32,116,104,101,32,109,111,
+    100,117,108,101,32,105,115,32,105,110,46,32,32,84,104,101,
+    32,112,97,114,101,110,116,32,105,115,32,100,101,114,105,118,
+    101,100,32,102,114,111,109,32,116,104,101,32,110,97,109,101,
+    46,10,10,32,32,32,32,96,105,115,95,112,97,99,107,97,
+    103,101,96,32,100,101,116,101,114,109,105,110,101,115,32,105,
+    102,32,116,104,101,32,109,111,100,117,108,101,32,105,115,32,
+    99,111,110,115,105,100,101,114,101,100,32,97,32,112,97,99,
+    107,97,103,101,32,111,114,10,32,32,32,32,110,111,116,46,
+    32,32,79,110,32,109,111,100,117,108,101,115,32,116,104,105,
+    115,32,105,115,32,114,101,102,108,101,99,116,101,100,32,98,
+    121,32,116,104,101,32,96,95,95,112,97,116,104,95,95,96,
+    32,97,116,116,114,105,98,117,116,101,46,10,10,32,32,32,
+    32,96,111,114,105,103,105,110,96,32,105,115,32,116,104,101,
+    32,115,112,101,99,105,102,105,99,32,108,111,99,97,116,105,
+    111,110,32,117,115,101,100,32,98,121,32,116,104,101,32,108,
+    111,97,100,101,114,32,102,114,111,109,32,119,104,105,99,104,
+    32,116,111,10,32,32,32,32,108,111,97,100,32,116,104,101,
+    32,109,111,100,117,108,101,44,32,105,102,32,116,104,97,116,
+    32,105,110,102,111,114,109,97,116,105,111,110,32,105,115,32,
+    97,118,97,105,108,97,98,108,101,46,32,32,87,104,101,110,
+    32,102,105,108,101,110,97,109,101,32,105,115,10,32,32,32,
+    32,115,101,116,44,32,111,114,105,103,105,110,32,119,105,108,
+    108,32,109,97,116,99,104,46,10,10,32,32,32,32,96,104,
+    97,115,95,108,111,99,97,116,105,111,110,96,32,105,110,100,
+    105,99,97,116,101,115,32,116,104,97,116,32,97,32,115,112,
+    101,99,39,115,32,34,111,114,105,103,105,110,34,32,114,101,
+    102,108,101,99,116,115,32,97,32,108,111,99,97,116,105,111,
+    110,46,10,32,32,32,32,87,104,101,110,32,116,104,105,115,
+    32,105,115,32,84,114,117,101,44,32,96,95,95,102,105,108,
+    101,95,95,96,32,97,116,116,114,105,98,117,116,101,32,111,
+    102,32,116,104,101,32,109,111,100,117,108,101,32,105,115,32,
+    115,101,116,46,10,10,32,32,32,32,96,99,97,99,104,101,
+    100,96,32,105,115,32,116,104,101,32,108,111,99,97,116,105,
+    111,110,32,111,102,32,116,104,101,32,99,97,99,104,101,100,
+    32,98,121,116,101,99,111,100,101,32,102,105,108,101,44,32,
+    105,102,32,97,110,121,46,32,32,73,116,10,32,32,32,32,
+    99,111,114,114,101,115,112,111,110,100,115,32,116,111,32,116,
+    104,101,32,96,95,95,99,97,99,104,101,100,95,95,96,32,
+    97,116,116,114,105,98,117,116,101,46,10,10,32,32,32,32,
+    96,115,117,98,109,111,100,117,108,101,95,115,101,97,114,99,
+    104,95,108,111,99,97,116,105,111,110,115,96,32,105,115,32,
+    116,104,101,32,115,101,113,117,101,110,99,101,32,111,102,32,
+    112,97,116,104,32,101,110,116,114,105,101,115,32,116,111,10,
+    32,32,32,32,115,101,97,114,99,104,32,119,104,101,110,32,
+    105,109,112,111,114,116,105,110,103,32,115,117,98,109,111,100,
+    117,108,101,115,46,32,32,73,102,32,115,101,116,44,32,105,
+    115,95,112,97,99,107,97,103,101,32,115,104,111,117,108,100,
+    32,98,101,10,32,32,32,32,84,114,117,101,45,45,97,110,
+    100,32,70,97,108,115,101,32,111,116,104,101,114,119,105,115,
+    101,46,10,10,32,32,32,32,80,97,99,107,97,103,101,115,
+    32,97,114,101,32,115,105,109,112,108,121,32,109,111,100,117,
+    108,101,115,32,116,104,97,116,32,40,109,97,121,41,32,104,
+    97,118,101,32,115,117,98,109,111,100,117,108,101,115,46,32,
+    32,73,102,32,97,32,115,112,101,99,10,32,32,32,32,104,
+    97,115,32,97,32,110,111,110,45,78,111,110,101,32,118,97,
+    108,117,101,32,105,110,32,96,115,117,98,109,111,100,117,108,
+    101,95,115,101,97,114,99,104,95,108,111,99,97,116,105,111,
+    110,115,96,44,32,116,104,101,32,105,109,112,111,114,116,10,
+    32,32,32,32,115,121,115,116,101,109,32,119,105,108,108,32,
+    99,111,110,115,105,100,101,114,32,109,111,100,117,108,101,115,
+    32,108,111,97,100,101,100,32,102,114,111,109,32,116,104,101,
+    32,115,112,101,99,32,97,115,32,112,97,99,107,97,103,101,
+    115,46,10,10,32,32,32,32,79,110,108,121,32,102,105,110,
+    100,101,114,115,32,40,115,101,101,32,105,109,112,111,114,116,
+    108,105,98,46,97,98,99,46,77,101,116,97,80,97,116,104,
+    70,105,110,100,101,114,32,97,110,100,10,32,32,32,32,105,
+    109,112,111,114,116,108,105,98,46,97,98,99,46,80,97,116,
+    104,69,110,116,114,121,70,105,110,100,101,114,41,32,115,104,
+    111,117,108,100,32,109,111,100,105,102,121,32,77,111,100,117,
+    108,101,83,112,101,99,32,105,110,115,116,97,110,99,101,115,
+    46,10,10,32,32,32,32,78,41,3,218,6,111,114,105,103,
+    105,110,218,12,108,111,97,100,101,114,95,115,116,97,116,101,
+    218,10,105,115,95,112,97,99,107,97,103,101,99,3,0,0,
+    0,3,0,0,0,6,0,0,0,2,0,0,0,67,0,0,
+    0,115,54,0,0,0,124,1,124,0,95,0,124,2,124,0,
+    95,1,124,3,124,0,95,2,124,4,124,0,95,3,124,5,
+    114,32,103,0,110,2,100,0,124,0,95,4,100,1,124,0,
+    95,5,100,0,124,0,95,6,100,0,83,0,41,2,78,70,
+    41,7,114,15,0,0,0,114,94,0,0,0,114,104,0,0,
+    0,114,105,0,0,0,218,26,115,117,98,109,111,100,117,108,
+    101,95,115,101,97,114,99,104,95,108,111,99,97,116,105,111,
+    110,115,218,13,95,115,101,116,95,102,105,108,101,97,116,116,
+    114,218,7,95,99,97,99,104,101,100,41,6,114,26,0,0,
+    0,114,15,0,0,0,114,94,0,0,0,114,104,0,0,0,
+    114,105,0,0,0,114,106,0,0,0,114,10,0,0,0,114,
+    10,0,0,0,114,11,0,0,0,114,27,0,0,0,113,1,
+    0,0,115,14,0,0,0,0,2,6,1,6,1,6,1,6,
+    1,14,3,6,1,122,19,77,111,100,117,108,101,83,112,101,
+    99,46,95,95,105,110,105,116,95,95,99,1,0,0,0,0,
+    0,0,0,2,0,0,0,4,0,0,0,67,0,0,0,115,
+    102,0,0,0,100,1,106,0,124,0,106,1,131,1,100,2,
+    106,0,124,0,106,2,131,1,103,2,125,1,124,0,106,3,
+    100,0,107,9,114,52,124,1,106,4,100,3,106,0,124,0,
+    106,3,131,1,131,1,1,0,124,0,106,5,100,0,107,9,
+    114,80,124,1,106,4,100,4,106,0,124,0,106,5,131,1,
+    131,1,1,0,100,5,106,0,124,0,106,6,106,7,100,6,
+    106,8,124,1,131,1,131,2,83,0,41,7,78,122,9,110,
+    97,109,101,61,123,33,114,125,122,11,108,111,97,100,101,114,
+    61,123,33,114,125,122,11,111,114,105,103,105,110,61,123,33,
+    114,125,122,29,115,117,98,109,111,100,117,108,101,95,115,101,
+    97,114,99,104,95,108,111,99,97,116,105,111,110,115,61,123,
+    125,122,6,123,125,40,123,125,41,122,2,44,32,41,9,114,
+    38,0,0,0,114,15,0,0,0,114,94,0,0,0,114,104,
+    0,0,0,218,6,97,112,112,101,110,100,114,107,0,0,0,
+    218,9,95,95,99,108,97,115,115,95,95,114,1,0,0,0,
+    218,4,106,111,105,110,41,2,114,26,0,0,0,114,47,0,
+    0,0,114,10,0,0,0,114,10,0,0,0,114,11,0,0,
+    0,114,40,0,0,0,125,1,0,0,115,16,0,0,0,0,
+    1,10,1,14,1,10,1,18,1,10,1,8,1,10,1,122,
+    19,77,111,100,117,108,101,83,112,101,99,46,95,95,114,101,
+    112,114,95,95,99,2,0,0,0,0,0,0,0,3,0,0,
+    0,11,0,0,0,67,0,0,0,115,102,0,0,0,124,0,
+    106,0,125,2,121,70,124,0,106,1,124,1,106,1,107,2,
+    111,76,124,0,106,2,124,1,106,2,107,2,111,76,124,0,
+    106,3,124,1,106,3,107,2,111,76,124,2,124,1,106,0,
+    107,2,111,76,124,0,106,4,124,1,106,4,107,2,111,76,
+    124,0,106,5,124,1,106,5,107,2,83,0,4,0,116,6,
+    107,10,114,96,1,0,1,0,1,0,100,1,83,0,88,0,
+    100,0,83,0,41,2,78,70,41,7,114,107,0,0,0,114,
+    15,0,0,0,114,94,0,0,0,114,104,0,0,0,218,6,
+    99,97,99,104,101,100,218,12,104,97,115,95,108,111,99,97,
+    116,105,111,110,114,91,0,0,0,41,3,114,26,0,0,0,
+    90,5,111,116,104,101,114,90,4,115,109,115,108,114,10,0,
+    0,0,114,10,0,0,0,114,11,0,0,0,218,6,95,95,
+    101,113,95,95,135,1,0,0,115,20,0,0,0,0,1,6,
+    1,2,1,12,1,12,1,12,1,10,1,12,1,12,1,14,
+    1,122,17,77,111,100,117,108,101,83,112,101,99,46,95,95,
+    101,113,95,95,99,1,0,0,0,0,0,0,0,1,0,0,
+    0,2,0,0,0,67,0,0,0,115,58,0,0,0,124,0,
+    106,0,100,0,107,8,114,52,124,0,106,1,100,0,107,9,
+    114,52,124,0,106,2,114,52,116,3,100,0,107,8,114,38,
+    116,4,130,1,116,3,106,5,124,0,106,1,131,1,124,0,
+    95,0,124,0,106,0,83,0,41,1,78,41,6,114,109,0,
+    0,0,114,104,0,0,0,114,108,0,0,0,218,19,95,98,
+    111,111,116,115,116,114,97,112,95,101,120,116,101,114,110,97,
+    108,218,19,78,111,116,73,109,112,108,101,109,101,110,116,101,
+    100,69,114,114,111,114,90,11,95,103,101,116,95,99,97,99,
+    104,101,100,41,1,114,26,0,0,0,114,10,0,0,0,114,
+    10,0,0,0,114,11,0,0,0,114,113,0,0,0,147,1,
+    0,0,115,12,0,0,0,0,2,10,1,16,1,8,1,4,
+    1,14,1,122,17,77,111,100,117,108,101,83,112,101,99,46,
+    99,97,99,104,101,100,99,2,0,0,0,0,0,0,0,2,
+    0,0,0,2,0,0,0,67,0,0,0,115,10,0,0,0,
+    124,1,124,0,95,0,100,0,83,0,41,1,78,41,1,114,
+    109,0,0,0,41,2,114,26,0,0,0,114,113,0,0,0,
+    114,10,0,0,0,114,10,0,0,0,114,11,0,0,0,114,
+    113,0,0,0,156,1,0,0,115,2,0,0,0,0,2,99,
+    1,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,
+    67,0,0,0,115,36,0,0,0,124,0,106,0,100,1,107,
+    8,114,26,124,0,106,1,106,2,100,2,131,1,100,3,25,
+    0,83,0,124,0,106,1,83,0,100,1,83,0,41,4,122,
+    32,84,104,101,32,110,97,109,101,32,111,102,32,116,104,101,
+    32,109,111,100,117,108,101,39,115,32,112,97,114,101,110,116,
+    46,78,218,1,46,114,19,0,0,0,41,3,114,107,0,0,
+    0,114,15,0,0,0,218,10,114,112,97,114,116,105,116,105,
+    111,110,41,1,114,26,0,0,0,114,10,0,0,0,114,10,
+    0,0,0,114,11,0,0,0,218,6,112,97,114,101,110,116,
+    160,1,0,0,115,6,0,0,0,0,3,10,1,16,2,122,
+    17,77,111,100,117,108,101,83,112,101,99,46,112,97,114,101,
+    110,116,99,1,0,0,0,0,0,0,0,1,0,0,0,1,
+    0,0,0,67,0,0,0,115,6,0,0,0,124,0,106,0,
+    83,0,41,1,78,41,1,114,108,0,0,0,41,1,114,26,
+    0,0,0,114,10,0,0,0,114,10,0,0,0,114,11,0,
+    0,0,114,114,0,0,0,168,1,0,0,115,2,0,0,0,
+    0,2,122,23,77,111,100,117,108,101,83,112,101,99,46,104,
+    97,115,95,108,111,99,97,116,105,111,110,99,2,0,0,0,
+    0,0,0,0,2,0,0,0,2,0,0,0,67,0,0,0,
+    115,14,0,0,0,116,0,124,1,131,1,124,0,95,1,100,
+    0,83,0,41,1,78,41,2,218,4,98,111,111,108,114,108,
+    0,0,0,41,2,114,26,0,0,0,218,5,118,97,108,117,
+    101,114,10,0,0,0,114,10,0,0,0,114,11,0,0,0,
+    114,114,0,0,0,172,1,0,0,115,2,0,0,0,0,2,
+    41,12,114,1,0,0,0,114,0,0,0,0,114,2,0,0,
+    0,114,3,0,0,0,114,27,0,0,0,114,40,0,0,0,
+    114,115,0,0,0,218,8,112,114,111,112,101,114,116,121,114,
+    113,0,0,0,218,6,115,101,116,116,101,114,114,120,0,0,
+    0,114,114,0,0,0,114,10,0,0,0,114,10,0,0,0,
+    114,10,0,0,0,114,11,0,0,0,114,103,0,0,0,76,
+    1,0,0,115,20,0,0,0,8,35,4,2,4,1,14,11,
+    8,10,8,12,12,9,14,4,12,8,12,4,114,103,0,0,
+    0,41,2,114,104,0,0,0,114,106,0,0,0,99,2,0,
+    0,0,2,0,0,0,6,0,0,0,14,0,0,0,67,0,
+    0,0,115,154,0,0,0,116,0,124,1,100,1,131,2,114,
+    74,116,1,100,2,107,8,114,22,116,2,130,1,116,1,106,
+    3,125,4,124,3,100,2,107,8,114,48,124,4,124,0,124,
+    1,100,3,141,2,83,0,124,3,114,56,103,0,110,2,100,
+    2,125,5,124,4,124,0,124,1,124,5,100,4,141,3,83,
+    0,124,3,100,2,107,8,114,138,116,0,124,1,100,5,131,
+    2,114,134,121,14,124,1,106,4,124,0,131,1,125,3,87,
+    0,113,138,4,0,116,5,107,10,114,130,1,0,1,0,1,
+    0,100,2,125,3,89,0,113,138,88,0,110,4,100,6,125,
+    3,116,6,124,0,124,1,124,2,124,3,100,7,141,4,83,
+    0,41,8,122,53,82,101,116,117,114,110,32,97,32,109,111,
+    100,117,108,101,32,115,112,101,99,32,98,97,115,101,100,32,
+    111,110,32,118,97,114,105,111,117,115,32,108,111,97,100,101,
+    114,32,109,101,116,104,111,100,115,46,90,12,103,101,116,95,
+    102,105,108,101,110,97,109,101,78,41,1,114,94,0,0,0,
+    41,2,114,94,0,0,0,114,107,0,0,0,114,106,0,0,
+    0,70,41,2,114,104,0,0,0,114,106,0,0,0,41,7,
+    114,4,0,0,0,114,116,0,0,0,114,117,0,0,0,218,
+    23,115,112,101,99,95,102,114,111,109,95,102,105,108,101,95,
+    108,111,99,97,116,105,111,110,114,106,0,0,0,114,71,0,
+    0,0,114,103,0,0,0,41,6,114,15,0,0,0,114,94,
+    0,0,0,114,104,0,0,0,114,106,0,0,0,114,125,0,
+    0,0,90,6,115,101,97,114,99,104,114,10,0,0,0,114,
+    10,0,0,0,114,11,0,0,0,114,79,0,0,0,177,1,
+    0,0,115,34,0,0,0,0,2,10,1,8,1,4,1,6,
+    2,8,1,12,1,12,1,6,1,8,2,8,1,10,1,2,
+    1,14,1,14,1,12,3,4,2,114,79,0,0,0,99,3,
+    0,0,0,0,0,0,0,8,0,0,0,53,0,0,0,67,
+    0,0,0,115,56,1,0,0,121,10,124,0,106,0,125,3,
+    87,0,110,20,4,0,116,1,107,10,114,30,1,0,1,0,
+    1,0,89,0,110,14,88,0,124,3,100,0,107,9,114,44,
+    124,3,83,0,124,0,106,2,125,4,124,1,100,0,107,8,
+    114,90,121,10,124,0,106,3,125,1,87,0,110,20,4,0,
+    116,1,107,10,114,88,1,0,1,0,1,0,89,0,110,2,
+    88,0,121,10,124,0,106,4,125,5,87,0,110,24,4,0,
+    116,1,107,10,114,124,1,0,1,0,1,0,100,0,125,5,
+    89,0,110,2,88,0,124,2,100,0,107,8,114,184,124,5,
+    100,0,107,8,114,180,121,10,124,1,106,5,125,2,87,0,
+    113,184,4,0,116,1,107,10,114,176,1,0,1,0,1,0,
+    100,0,125,2,89,0,113,184,88,0,110,4,124,5,125,2,
+    121,10,124,0,106,6,125,6,87,0,110,24,4,0,116,1,
+    107,10,114,218,1,0,1,0,1,0,100,0,125,6,89,0,
+    110,2,88,0,121,14,116,7,124,0,106,8,131,1,125,7,
+    87,0,110,26,4,0,116,1,107,10,144,1,114,4,1,0,
+    1,0,1,0,100,0,125,7,89,0,110,2,88,0,116,9,
+    124,4,124,1,124,2,100,1,141,3,125,3,124,5,100,0,
+    107,8,144,1,114,34,100,2,110,2,100,3,124,3,95,10,
+    124,6,124,3,95,11,124,7,124,3,95,12,124,3,83,0,
+    41,4,78,41,1,114,104,0,0,0,70,84,41,13,114,90,
+    0,0,0,114,91,0,0,0,114,1,0,0,0,114,86,0,
+    0,0,114,93,0,0,0,90,7,95,79,82,73,71,73,78,
+    218,10,95,95,99,97,99,104,101,100,95,95,218,4,108,105,
+    115,116,218,8,95,95,112,97,116,104,95,95,114,103,0,0,
+    0,114,108,0,0,0,114,113,0,0,0,114,107,0,0,0,
+    41,8,114,84,0,0,0,114,94,0,0,0,114,104,0,0,
+    0,114,83,0,0,0,114,15,0,0,0,90,8,108,111,99,
+    97,116,105,111,110,114,113,0,0,0,114,107,0,0,0,114,
+    10,0,0,0,114,10,0,0,0,114,11,0,0,0,218,17,
+    95,115,112,101,99,95,102,114,111,109,95,109,111,100,117,108,
+    101,203,1,0,0,115,72,0,0,0,0,2,2,1,10,1,
+    14,1,6,2,8,1,4,2,6,1,8,1,2,1,10,1,
+    14,2,6,1,2,1,10,1,14,1,10,1,8,1,8,1,
+    2,1,10,1,14,1,12,2,4,1,2,1,10,1,14,1,
+    10,1,2,1,14,1,16,1,10,2,14,1,20,1,6,1,
+    6,1,114,129,0,0,0,70,41,1,218,8,111,118,101,114,
+    114,105,100,101,99,2,0,0,0,1,0,0,0,5,0,0,
+    0,59,0,0,0,67,0,0,0,115,212,1,0,0,124,2,
+    115,20,116,0,124,1,100,1,100,0,131,3,100,0,107,8,
+    114,54,121,12,124,0,106,1,124,1,95,2,87,0,110,20,
+    4,0,116,3,107,10,114,52,1,0,1,0,1,0,89,0,
+    110,2,88,0,124,2,115,74,116,0,124,1,100,2,100,0,
+    131,3,100,0,107,8,114,166,124,0,106,4,125,3,124,3,
+    100,0,107,8,114,134,124,0,106,5,100,0,107,9,114,134,
+    116,6,100,0,107,8,114,110,116,7,130,1,116,6,106,8,
+    125,4,124,4,106,9,124,4,131,1,125,3,124,0,106,5,
+    124,3,95,10,121,10,124,3,124,1,95,11,87,0,110,20,
+    4,0,116,3,107,10,114,164,1,0,1,0,1,0,89,0,
+    110,2,88,0,124,2,115,186,116,0,124,1,100,3,100,0,
+    131,3,100,0,107,8,114,220,121,12,124,0,106,12,124,1,
+    95,13,87,0,110,20,4,0,116,3,107,10,114,218,1,0,
+    1,0,1,0,89,0,110,2,88,0,121,10,124,0,124,1,
+    95,14,87,0,110,20,4,0,116,3,107,10,114,250,1,0,
+    1,0,1,0,89,0,110,2,88,0,124,2,144,1,115,20,
+    116,0,124,1,100,4,100,0,131,3,100,0,107,8,144,1,
+    114,68,124,0,106,5,100,0,107,9,144,1,114,68,121,12,
+    124,0,106,5,124,1,95,15,87,0,110,22,4,0,116,3,
+    107,10,144,1,114,66,1,0,1,0,1,0,89,0,110,2,
+    88,0,124,0,106,16,144,1,114,208,124,2,144,1,115,100,
+    116,0,124,1,100,5,100,0,131,3,100,0,107,8,144,1,
+    114,136,121,12,124,0,106,17,124,1,95,18,87,0,110,22,
+    4,0,116,3,107,10,144,1,114,134,1,0,1,0,1,0,
+    89,0,110,2,88,0,124,2,144,1,115,160,116,0,124,1,
+    100,6,100,0,131,3,100,0,107,8,144,1,114,208,124,0,
+    106,19,100,0,107,9,144,1,114,208,121,12,124,0,106,19,
+    124,1,95,20,87,0,110,22,4,0,116,3,107,10,144,1,
+    114,206,1,0,1,0,1,0,89,0,110,2,88,0,124,1,
+    83,0,41,7,78,114,1,0,0,0,114,86,0,0,0,218,
+    11,95,95,112,97,99,107,97,103,101,95,95,114,128,0,0,
+    0,114,93,0,0,0,114,126,0,0,0,41,21,114,6,0,
+    0,0,114,15,0,0,0,114,1,0,0,0,114,91,0,0,
+    0,114,94,0,0,0,114,107,0,0,0,114,116,0,0,0,
+    114,117,0,0,0,218,16,95,78,97,109,101,115,112,97,99,
+    101,76,111,97,100,101,114,218,7,95,95,110,101,119,95,95,
+    90,5,95,112,97,116,104,114,86,0,0,0,114,120,0,0,
+    0,114,131,0,0,0,114,90,0,0,0,114,128,0,0,0,
+    114,114,0,0,0,114,104,0,0,0,114,93,0,0,0,114,
+    113,0,0,0,114,126,0,0,0,41,5,114,83,0,0,0,
+    114,84,0,0,0,114,130,0,0,0,114,94,0,0,0,114,
+    132,0,0,0,114,10,0,0,0,114,10,0,0,0,114,11,
+    0,0,0,218,18,95,105,110,105,116,95,109,111,100,117,108,
+    101,95,97,116,116,114,115,248,1,0,0,115,92,0,0,0,
+    0,4,20,1,2,1,12,1,14,1,6,2,20,1,6,1,
+    8,2,10,1,8,1,4,1,6,2,10,1,8,1,2,1,
+    10,1,14,1,6,2,20,1,2,1,12,1,14,1,6,2,
+    2,1,10,1,14,1,6,2,24,1,12,1,2,1,12,1,
+    16,1,6,2,8,1,24,1,2,1,12,1,16,1,6,2,
+    24,1,12,1,2,1,12,1,16,1,6,1,114,134,0,0,
+    0,99,1,0,0,0,0,0,0,0,2,0,0,0,3,0,
+    0,0,67,0,0,0,115,82,0,0,0,100,1,125,1,116,
+    0,124,0,106,1,100,2,131,2,114,30,124,0,106,1,106,
+    2,124,0,131,1,125,1,110,20,116,0,124,0,106,1,100,
+    3,131,2,114,50,116,3,100,4,131,1,130,1,124,1,100,
+    1,107,8,114,68,116,4,124,0,106,5,131,1,125,1,116,
+    6,124,0,124,1,131,2,1,0,124,1,83,0,41,5,122,
+    43,67,114,101,97,116,101,32,97,32,109,111,100,117,108,101,
+    32,98,97,115,101,100,32,111,110,32,116,104,101,32,112,114,
+    111,118,105,100,101,100,32,115,112,101,99,46,78,218,13,99,
+    114,101,97,116,101,95,109,111,100,117,108,101,218,11,101,120,
+    101,99,95,109,111,100,117,108,101,122,66,108,111,97,100,101,
+    114,115,32,116,104,97,116,32,100,101,102,105,110,101,32,101,
+    120,101,99,95,109,111,100,117,108,101,40,41,32,109,117,115,
+    116,32,97,108,115,111,32,100,101,102,105,110,101,32,99,114,
+    101,97,116,101,95,109,111,100,117,108,101,40,41,41,7,114,
+    4,0,0,0,114,94,0,0,0,114,135,0,0,0,114,71,
+    0,0,0,114,16,0,0,0,114,15,0,0,0,114,134,0,
+    0,0,41,2,114,83,0,0,0,114,84,0,0,0,114,10,
+    0,0,0,114,10,0,0,0,114,11,0,0,0,218,16,109,
+    111,100,117,108,101,95,102,114,111,109,95,115,112,101,99,52,
+    2,0,0,115,18,0,0,0,0,3,4,1,12,3,14,1,
+    12,1,8,2,8,1,10,1,10,1,114,137,0,0,0,99,
     1,0,0,0,0,0,0,0,2,0,0,0,3,0,0,0,
-    3,0,0,0,115,26,0,0,0,135,0,102,1,100,1,100,
-    2,132,8,125,1,116,0,124,1,136,0,131,2,1,0,124,
-    1,83,0,41,3,122,47,68,101,99,111,114,97,116,111,114,
-    32,116,111,32,118,101,114,105,102,121,32,116,104,101,32,110,
-    97,109,101,100,32,109,111,100,117,108,101,32,105,115,32,102,
-    114,111,122,101,110,46,99,2,0,0,0,0,0,0,0,2,
-    0,0,0,4,0,0,0,19,0,0,0,115,38,0,0,0,
-    116,0,106,1,124,1,131,1,115,28,116,2,100,1,106,3,
-    124,1,131,1,124,1,100,2,141,2,130,1,136,0,124,0,
-    124,1,131,2,83,0,41,3,78,122,27,123,33,114,125,32,
-    105,115,32,110,111,116,32,97,32,102,114,111,122,101,110,32,
-    109,111,100,117,108,101,41,1,114,15,0,0,0,41,4,114,
-    46,0,0,0,218,9,105,115,95,102,114,111,122,101,110,114,
-    70,0,0,0,114,38,0,0,0,41,2,114,26,0,0,0,
-    114,71,0,0,0,41,1,114,72,0,0,0,114,10,0,0,
-    0,114,11,0,0,0,218,24,95,114,101,113,117,105,114,101,
-    115,95,102,114,111,122,101,110,95,119,114,97,112,112,101,114,
-    229,0,0,0,115,8,0,0,0,0,1,10,1,10,1,8,
-    1,122,50,95,114,101,113,117,105,114,101,115,95,102,114,111,
-    122,101,110,46,60,108,111,99,97,108,115,62,46,95,114,101,
-    113,117,105,114,101,115,95,102,114,111,122,101,110,95,119,114,
-    97,112,112,101,114,41,1,114,12,0,0,0,41,2,114,72,
-    0,0,0,114,76,0,0,0,114,10,0,0,0,41,1,114,
-    72,0,0,0,114,11,0,0,0,218,16,95,114,101,113,117,
-    105,114,101,115,95,102,114,111,122,101,110,227,0,0,0,115,
-    6,0,0,0,0,2,12,5,10,1,114,77,0,0,0,99,
-    2,0,0,0,0,0,0,0,4,0,0,0,3,0,0,0,
-    67,0,0,0,115,62,0,0,0,116,0,124,1,124,0,131,
-    2,125,2,124,1,116,1,106,2,107,6,114,50,116,1,106,
-    2,124,1,25,0,125,3,116,3,124,2,124,3,131,2,1,
-    0,116,1,106,2,124,1,25,0,83,0,116,4,124,2,131,
-    1,83,0,100,1,83,0,41,2,122,128,76,111,97,100,32,
-    116,104,101,32,115,112,101,99,105,102,105,101,100,32,109,111,
-    100,117,108,101,32,105,110,116,111,32,115,121,115,46,109,111,
-    100,117,108,101,115,32,97,110,100,32,114,101,116,117,114,110,
-    32,105,116,46,10,10,32,32,32,32,84,104,105,115,32,109,
-    101,116,104,111,100,32,105,115,32,100,101,112,114,101,99,97,
-    116,101,100,46,32,32,85,115,101,32,108,111,97,100,101,114,
-    46,101,120,101,99,95,109,111,100,117,108,101,32,105,110,115,
-    116,101,97,100,46,10,10,32,32,32,32,78,41,5,218,16,
-    115,112,101,99,95,102,114,111,109,95,108,111,97,100,101,114,
-    114,14,0,0,0,218,7,109,111,100,117,108,101,115,218,5,
-    95,101,120,101,99,218,5,95,108,111,97,100,41,4,114,26,
-    0,0,0,114,71,0,0,0,218,4,115,112,101,99,218,6,
-    109,111,100,117,108,101,114,10,0,0,0,114,10,0,0,0,
-    114,11,0,0,0,218,17,95,108,111,97,100,95,109,111,100,
-    117,108,101,95,115,104,105,109,239,0,0,0,115,12,0,0,
-    0,0,6,10,1,10,1,10,1,10,1,10,2,114,84,0,
-    0,0,99,1,0,0,0,0,0,0,0,5,0,0,0,35,
-    0,0,0,67,0,0,0,115,216,0,0,0,116,0,124,0,
-    100,1,100,0,131,3,125,1,116,1,124,1,100,2,131,2,
-    114,54,121,10,124,1,106,2,124,0,131,1,83,0,4,0,
-    116,3,107,10,114,52,1,0,1,0,1,0,89,0,110,2,
-    88,0,121,10,124,0,106,4,125,2,87,0,110,20,4,0,
-    116,5,107,10,114,84,1,0,1,0,1,0,89,0,110,18,
-    88,0,124,2,100,0,107,9,114,102,116,6,124,2,131,1,
-    83,0,121,10,124,0,106,7,125,3,87,0,110,24,4,0,
-    116,5,107,10,114,136,1,0,1,0,1,0,100,3,125,3,
-    89,0,110,2,88,0,121,10,124,0,106,8,125,4,87,0,
-    110,50,4,0,116,5,107,10,114,198,1,0,1,0,1,0,
-    124,1,100,0,107,8,114,182,100,4,106,9,124,3,131,1,
-    83,0,100,5,106,9,124,3,124,1,131,2,83,0,89,0,
-    110,14,88,0,100,6,106,9,124,3,124,4,131,2,83,0,
-    100,0,83,0,41,7,78,218,10,95,95,108,111,97,100,101,
-    114,95,95,218,11,109,111,100,117,108,101,95,114,101,112,114,
-    250,1,63,122,13,60,109,111,100,117,108,101,32,123,33,114,
+    67,0,0,0,115,106,0,0,0,124,0,106,0,100,1,107,
+    8,114,14,100,2,110,4,124,0,106,0,125,1,124,0,106,
+    1,100,1,107,8,114,66,124,0,106,2,100,1,107,8,114,
+    50,100,3,106,3,124,1,131,1,83,0,100,4,106,3,124,
+    1,124,0,106,2,131,2,83,0,110,36,124,0,106,4,114,
+    86,100,5,106,3,124,1,124,0,106,1,131,2,83,0,100,
+    6,106,3,124,0,106,0,124,0,106,1,131,2,83,0,100,
+    1,83,0,41,7,122,38,82,101,116,117,114,110,32,116,104,
+    101,32,114,101,112,114,32,116,111,32,117,115,101,32,102,111,
+    114,32,116,104,101,32,109,111,100,117,108,101,46,78,114,88,
+    0,0,0,122,13,60,109,111,100,117,108,101,32,123,33,114,
     125,62,122,20,60,109,111,100,117,108,101,32,123,33,114,125,
     32,40,123,33,114,125,41,62,122,23,60,109,111,100,117,108,
     101,32,123,33,114,125,32,102,114,111,109,32,123,33,114,125,
-    62,41,10,114,6,0,0,0,114,4,0,0,0,114,86,0,
-    0,0,218,9,69,120,99,101,112,116,105,111,110,218,8,95,
-    95,115,112,101,99,95,95,218,14,65,116,116,114,105,98,117,
-    116,101,69,114,114,111,114,218,22,95,109,111,100,117,108,101,
-    95,114,101,112,114,95,102,114,111,109,95,115,112,101,99,114,
-    1,0,0,0,218,8,95,95,102,105,108,101,95,95,114,38,
-    0,0,0,41,5,114,83,0,0,0,218,6,108,111,97,100,
-    101,114,114,82,0,0,0,114,15,0,0,0,218,8,102,105,
-    108,101,110,97,109,101,114,10,0,0,0,114,10,0,0,0,
-    114,11,0,0,0,218,12,95,109,111,100,117,108,101,95,114,
-    101,112,114,255,0,0,0,115,46,0,0,0,0,2,12,1,
-    10,4,2,1,10,1,14,1,6,1,2,1,10,1,14,1,
-    6,2,8,1,8,4,2,1,10,1,14,1,10,1,2,1,
-    10,1,14,1,8,1,10,2,18,2,114,95,0,0,0,99,
-    0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,
-    64,0,0,0,115,36,0,0,0,101,0,90,1,100,0,90,
-    2,100,1,100,2,132,0,90,3,100,3,100,4,132,0,90,
-    4,100,5,100,6,132,0,90,5,100,7,83,0,41,8,218,
-    17,95,105,110,115,116,97,108,108,101,100,95,115,97,102,101,
-    108,121,99,2,0,0,0,0,0,0,0,2,0,0,0,2,
-    0,0,0,67,0,0,0,115,18,0,0,0,124,1,124,0,
-    95,0,124,1,106,1,124,0,95,2,100,0,83,0,41,1,
-    78,41,3,218,7,95,109,111,100,117,108,101,114,89,0,0,
-    0,218,5,95,115,112,101,99,41,2,114,26,0,0,0,114,
-    83,0,0,0,114,10,0,0,0,114,10,0,0,0,114,11,
-    0,0,0,114,27,0,0,0,37,1,0,0,115,4,0,0,
-    0,0,1,6,1,122,26,95,105,110,115,116,97,108,108,101,
-    100,95,115,97,102,101,108,121,46,95,95,105,110,105,116,95,
-    95,99,1,0,0,0,0,0,0,0,1,0,0,0,3,0,
-    0,0,67,0,0,0,115,28,0,0,0,100,1,124,0,106,
-    0,95,1,124,0,106,2,116,3,106,4,124,0,106,0,106,
-    5,60,0,100,0,83,0,41,2,78,84,41,6,114,98,0,
-    0,0,218,13,95,105,110,105,116,105,97,108,105,122,105,110,
-    103,114,97,0,0,0,114,14,0,0,0,114,79,0,0,0,
-    114,15,0,0,0,41,1,114,26,0,0,0,114,10,0,0,
-    0,114,10,0,0,0,114,11,0,0,0,114,48,0,0,0,
-    41,1,0,0,115,4,0,0,0,0,4,8,1,122,27,95,
-    105,110,115,116,97,108,108,101,100,95,115,97,102,101,108,121,
-    46,95,95,101,110,116,101,114,95,95,99,1,0,0,0,0,
-    0,0,0,3,0,0,0,17,0,0,0,71,0,0,0,115,
-    98,0,0,0,122,82,124,0,106,0,125,2,116,1,100,1,
-    100,2,132,0,124,1,68,0,131,1,131,1,114,64,121,14,
-    116,2,106,3,124,2,106,4,61,0,87,0,113,80,4,0,
-    116,5,107,10,114,60,1,0,1,0,1,0,89,0,113,80,
-    88,0,110,16,116,6,100,3,124,2,106,4,124,2,106,7,
-    131,3,1,0,87,0,100,0,100,4,124,0,106,0,95,8,
-    88,0,100,0,83,0,41,5,78,99,1,0,0,0,0,0,
-    0,0,2,0,0,0,3,0,0,0,115,0,0,0,115,22,
-    0,0,0,124,0,93,14,125,1,124,1,100,0,107,9,86,
-    0,1,0,113,2,100,0,83,0,41,1,78,114,10,0,0,
-    0,41,2,90,2,46,48,90,3,97,114,103,114,10,0,0,
-    0,114,10,0,0,0,114,11,0,0,0,250,9,60,103,101,
-    110,101,120,112,114,62,51,1,0,0,115,2,0,0,0,4,
-    0,122,45,95,105,110,115,116,97,108,108,101,100,95,115,97,
-    102,101,108,121,46,95,95,101,120,105,116,95,95,46,60,108,
-    111,99,97,108,115,62,46,60,103,101,110,101,120,112,114,62,
-    122,18,105,109,112,111,114,116,32,123,33,114,125,32,35,32,
-    123,33,114,125,70,41,9,114,98,0,0,0,218,3,97,110,
-    121,114,14,0,0,0,114,79,0,0,0,114,15,0,0,0,
-    114,54,0,0,0,114,68,0,0,0,114,93,0,0,0,114,
-    99,0,0,0,41,3,114,26,0,0,0,114,49,0,0,0,
-    114,82,0,0,0,114,10,0,0,0,114,10,0,0,0,114,
-    11,0,0,0,114,50,0,0,0,48,1,0,0,115,18,0,
-    0,0,0,1,2,1,6,1,18,1,2,1,14,1,14,1,
-    8,2,20,2,122,26,95,105,110,115,116,97,108,108,101,100,
-    95,115,97,102,101,108,121,46,95,95,101,120,105,116,95,95,
-    78,41,6,114,1,0,0,0,114,0,0,0,0,114,2,0,
-    0,0,114,27,0,0,0,114,48,0,0,0,114,50,0,0,
-    0,114,10,0,0,0,114,10,0,0,0,114,10,0,0,0,
-    114,11,0,0,0,114,96,0,0,0,35,1,0,0,115,6,
-    0,0,0,8,2,8,4,8,7,114,96,0,0,0,99,0,
-    0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,64,
-    0,0,0,115,114,0,0,0,101,0,90,1,100,0,90,2,
-    100,1,90,3,100,2,100,2,100,2,100,3,156,3,100,4,
-    100,5,132,2,90,4,100,6,100,7,132,0,90,5,100,8,
-    100,9,132,0,90,6,101,7,100,10,100,11,132,0,131,1,
-    90,8,101,8,106,9,100,12,100,11,132,0,131,1,90,8,
-    101,7,100,13,100,14,132,0,131,1,90,10,101,7,100,15,
-    100,16,132,0,131,1,90,11,101,11,106,9,100,17,100,16,
-    132,0,131,1,90,11,100,2,83,0,41,18,218,10,77,111,
-    100,117,108,101,83,112,101,99,97,208,5,0,0,84,104,101,
-    32,115,112,101,99,105,102,105,99,97,116,105,111,110,32,102,
-    111,114,32,97,32,109,111,100,117,108,101,44,32,117,115,101,
-    100,32,102,111,114,32,108,111,97,100,105,110,103,46,10,10,
-    32,32,32,32,65,32,109,111,100,117,108,101,39,115,32,115,
-    112,101,99,32,105,115,32,116,104,101,32,115,111,117,114,99,
-    101,32,102,111,114,32,105,110,102,111,114,109,97,116,105,111,
-    110,32,97,98,111,117,116,32,116,104,101,32,109,111,100,117,
-    108,101,46,32,32,70,111,114,10,32,32,32,32,100,97,116,
-    97,32,97,115,115,111,99,105,97,116,101,100,32,119,105,116,
-    104,32,116,104,101,32,109,111,100,117,108,101,44,32,105,110,
-    99,108,117,100,105,110,103,32,115,111,117,114,99,101,44,32,
-    117,115,101,32,116,104,101,32,115,112,101,99,39,115,10,32,
-    32,32,32,108,111,97,100,101,114,46,10,10,32,32,32,32,
-    96,110,97,109,101,96,32,105,115,32,116,104,101,32,97,98,
-    115,111,108,117,116,101,32,110,97,109,101,32,111,102,32,116,
-    104,101,32,109,111,100,117,108,101,46,32,32,96,108,111,97,
-    100,101,114,96,32,105,115,32,116,104,101,32,108,111,97,100,
-    101,114,10,32,32,32,32,116,111,32,117,115,101,32,119,104,
-    101,110,32,108,111,97,100,105,110,103,32,116,104,101,32,109,
-    111,100,117,108,101,46,32,32,96,112,97,114,101,110,116,96,
-    32,105,115,32,116,104,101,32,110,97,109,101,32,111,102,32,
-    116,104,101,10,32,32,32,32,112,97,99,107,97,103,101,32,
-    116,104,101,32,109,111,100,117,108,101,32,105,115,32,105,110,
-    46,32,32,84,104,101,32,112,97,114,101,110,116,32,105,115,
-    32,100,101,114,105,118,101,100,32,102,114,111,109,32,116,104,
-    101,32,110,97,109,101,46,10,10,32,32,32,32,96,105,115,
-    95,112,97,99,107,97,103,101,96,32,100,101,116,101,114,109,
-    105,110,101,115,32,105,102,32,116,104,101,32,109,111,100,117,
-    108,101,32,105,115,32,99,111,110,115,105,100,101,114,101,100,
-    32,97,32,112,97,99,107,97,103,101,32,111,114,10,32,32,
-    32,32,110,111,116,46,32,32,79,110,32,109,111,100,117,108,
-    101,115,32,116,104,105,115,32,105,115,32,114,101,102,108,101,
-    99,116,101,100,32,98,121,32,116,104,101,32,96,95,95,112,
-    97,116,104,95,95,96,32,97,116,116,114,105,98,117,116,101,
-    46,10,10,32,32,32,32,96,111,114,105,103,105,110,96,32,
-    105,115,32,116,104,101,32,115,112,101,99,105,102,105,99,32,
-    108,111,99,97,116,105,111,110,32,117,115,101,100,32,98,121,
-    32,116,104,101,32,108,111,97,100,101,114,32,102,114,111,109,
-    32,119,104,105,99,104,32,116,111,10,32,32,32,32,108,111,
-    97,100,32,116,104,101,32,109,111,100,117,108,101,44,32,105,
-    102,32,116,104,97,116,32,105,110,102,111,114,109,97,116,105,
-    111,110,32,105,115,32,97,118,97,105,108,97,98,108,101,46,
-    32,32,87,104,101,110,32,102,105,108,101,110,97,109,101,32,
-    105,115,10,32,32,32,32,115,101,116,44,32,111,114,105,103,
-    105,110,32,119,105,108,108,32,109,97,116,99,104,46,10,10,
-    32,32,32,32,96,104,97,115,95,108,111,99,97,116,105,111,
-    110,96,32,105,110,100,105,99,97,116,101,115,32,116,104,97,
-    116,32,97,32,115,112,101,99,39,115,32,34,111,114,105,103,
-    105,110,34,32,114,101,102,108,101,99,116,115,32,97,32,108,
-    111,99,97,116,105,111,110,46,10,32,32,32,32,87,104,101,
-    110,32,116,104,105,115,32,105,115,32,84,114,117,101,44,32,
-    96,95,95,102,105,108,101,95,95,96,32,97,116,116,114,105,
-    98,117,116,101,32,111,102,32,116,104,101,32,109,111,100,117,
-    108,101,32,105,115,32,115,101,116,46,10,10,32,32,32,32,
-    96,99,97,99,104,101,100,96,32,105,115,32,116,104,101,32,
-    108,111,99,97,116,105,111,110,32,111,102,32,116,104,101,32,
-    99,97,99,104,101,100,32,98,121,116,101,99,111,100,101,32,
-    102,105,108,101,44,32,105,102,32,97,110,121,46,32,32,73,
-    116,10,32,32,32,32,99,111,114,114,101,115,112,111,110,100,
-    115,32,116,111,32,116,104,101,32,96,95,95,99,97,99,104,
-    101,100,95,95,96,32,97,116,116,114,105,98,117,116,101,46,
-    10,10,32,32,32,32,96,115,117,98,109,111,100,117,108,101,
-    95,115,101,97,114,99,104,95,108,111,99,97,116,105,111,110,
-    115,96,32,105,115,32,116,104,101,32,115,101,113,117,101,110,
-    99,101,32,111,102,32,112,97,116,104,32,101,110,116,114,105,
-    101,115,32,116,111,10,32,32,32,32,115,101,97,114,99,104,
-    32,119,104,101,110,32,105,109,112,111,114,116,105,110,103,32,
-    115,117,98,109,111,100,117,108,101,115,46,32,32,73,102,32,
-    115,101,116,44,32,105,115,95,112,97,99,107,97,103,101,32,
-    115,104,111,117,108,100,32,98,101,10,32,32,32,32,84,114,
-    117,101,45,45,97,110,100,32,70,97,108,115,101,32,111,116,
-    104,101,114,119,105,115,101,46,10,10,32,32,32,32,80,97,
-    99,107,97,103,101,115,32,97,114,101,32,115,105,109,112,108,
-    121,32,109,111,100,117,108,101,115,32,116,104,97,116,32,40,
-    109,97,121,41,32,104,97,118,101,32,115,117,98,109,111,100,
-    117,108,101,115,46,32,32,73,102,32,97,32,115,112,101,99,
-    10,32,32,32,32,104,97,115,32,97,32,110,111,110,45,78,
-    111,110,101,32,118,97,108,117,101,32,105,110,32,96,115,117,
-    98,109,111,100,117,108,101,95,115,101,97,114,99,104,95,108,
-    111,99,97,116,105,111,110,115,96,44,32,116,104,101,32,105,
-    109,112,111,114,116,10,32,32,32,32,115,121,115,116,101,109,
-    32,119,105,108,108,32,99,111,110,115,105,100,101,114,32,109,
-    111,100,117,108,101,115,32,108,111,97,100,101,100,32,102,114,
-    111,109,32,116,104,101,32,115,112,101,99,32,97,115,32,112,
-    97,99,107,97,103,101,115,46,10,10,32,32,32,32,79,110,
-    108,121,32,102,105,110,100,101,114,115,32,40,115,101,101,32,
-    105,109,112,111,114,116,108,105,98,46,97,98,99,46,77,101,
-    116,97,80,97,116,104,70,105,110,100,101,114,32,97,110,100,
-    10,32,32,32,32,105,109,112,111,114,116,108,105,98,46,97,
-    98,99,46,80,97,116,104,69,110,116,114,121,70,105,110,100,
-    101,114,41,32,115,104,111,117,108,100,32,109,111,100,105,102,
-    121,32,77,111,100,117,108,101,83,112,101,99,32,105,110,115,
-    116,97,110,99,101,115,46,10,10,32,32,32,32,78,41,3,
-    218,6,111,114,105,103,105,110,218,12,108,111,97,100,101,114,
-    95,115,116,97,116,101,218,10,105,115,95,112,97,99,107,97,
-    103,101,99,3,0,0,0,3,0,0,0,6,0,0,0,2,
-    0,0,0,67,0,0,0,115,54,0,0,0,124,1,124,0,
-    95,0,124,2,124,0,95,1,124,3,124,0,95,2,124,4,
-    124,0,95,3,124,5,114,32,103,0,110,2,100,0,124,0,
-    95,4,100,1,124,0,95,5,100,0,124,0,95,6,100,0,
-    83,0,41,2,78,70,41,7,114,15,0,0,0,114,93,0,
-    0,0,114,103,0,0,0,114,104,0,0,0,218,26,115,117,
-    98,109,111,100,117,108,101,95,115,101,97,114,99,104,95,108,
-    111,99,97,116,105,111,110,115,218,13,95,115,101,116,95,102,
-    105,108,101,97,116,116,114,218,7,95,99,97,99,104,101,100,
-    41,6,114,26,0,0,0,114,15,0,0,0,114,93,0,0,
-    0,114,103,0,0,0,114,104,0,0,0,114,105,0,0,0,
-    114,10,0,0,0,114,10,0,0,0,114,11,0,0,0,114,
-    27,0,0,0,99,1,0,0,115,14,0,0,0,0,2,6,
-    1,6,1,6,1,6,1,14,3,6,1,122,19,77,111,100,
-    117,108,101,83,112,101,99,46,95,95,105,110,105,116,95,95,
-    99,1,0,0,0,0,0,0,0,2,0,0,0,4,0,0,
-    0,67,0,0,0,115,102,0,0,0,100,1,106,0,124,0,
-    106,1,131,1,100,2,106,0,124,0,106,2,131,1,103,2,
-    125,1,124,0,106,3,100,0,107,9,114,52,124,1,106,4,
-    100,3,106,0,124,0,106,3,131,1,131,1,1,0,124,0,
-    106,5,100,0,107,9,114,80,124,1,106,4,100,4,106,0,
-    124,0,106,5,131,1,131,1,1,0,100,5,106,0,124,0,
-    106,6,106,7,100,6,106,8,124,1,131,1,131,2,83,0,
-    41,7,78,122,9,110,97,109,101,61,123,33,114,125,122,11,
-    108,111,97,100,101,114,61,123,33,114,125,122,11,111,114,105,
-    103,105,110,61,123,33,114,125,122,29,115,117,98,109,111,100,
-    117,108,101,95,115,101,97,114,99,104,95,108,111,99,97,116,
-    105,111,110,115,61,123,125,122,6,123,125,40,123,125,41,122,
-    2,44,32,41,9,114,38,0,0,0,114,15,0,0,0,114,
-    93,0,0,0,114,103,0,0,0,218,6,97,112,112,101,110,
-    100,114,106,0,0,0,218,9,95,95,99,108,97,115,115,95,
-    95,114,1,0,0,0,218,4,106,111,105,110,41,2,114,26,
-    0,0,0,114,49,0,0,0,114,10,0,0,0,114,10,0,
-    0,0,114,11,0,0,0,114,40,0,0,0,111,1,0,0,
-    115,16,0,0,0,0,1,10,1,14,1,10,1,18,1,10,
-    1,8,1,10,1,122,19,77,111,100,117,108,101,83,112,101,
-    99,46,95,95,114,101,112,114,95,95,99,2,0,0,0,0,
-    0,0,0,3,0,0,0,11,0,0,0,67,0,0,0,115,
-    102,0,0,0,124,0,106,0,125,2,121,70,124,0,106,1,
-    124,1,106,1,107,2,111,76,124,0,106,2,124,1,106,2,
-    107,2,111,76,124,0,106,3,124,1,106,3,107,2,111,76,
-    124,2,124,1,106,0,107,2,111,76,124,0,106,4,124,1,
-    106,4,107,2,111,76,124,0,106,5,124,1,106,5,107,2,
-    83,0,4,0,116,6,107,10,114,96,1,0,1,0,1,0,
-    100,1,83,0,88,0,100,0,83,0,41,2,78,70,41,7,
-    114,106,0,0,0,114,15,0,0,0,114,93,0,0,0,114,
-    103,0,0,0,218,6,99,97,99,104,101,100,218,12,104,97,
-    115,95,108,111,99,97,116,105,111,110,114,90,0,0,0,41,
-    3,114,26,0,0,0,90,5,111,116,104,101,114,90,4,115,
-    109,115,108,114,10,0,0,0,114,10,0,0,0,114,11,0,
-    0,0,218,6,95,95,101,113,95,95,121,1,0,0,115,20,
-    0,0,0,0,1,6,1,2,1,12,1,12,1,12,1,10,
-    1,12,1,12,1,14,1,122,17,77,111,100,117,108,101,83,
-    112,101,99,46,95,95,101,113,95,95,99,1,0,0,0,0,
-    0,0,0,1,0,0,0,2,0,0,0,67,0,0,0,115,
-    58,0,0,0,124,0,106,0,100,0,107,8,114,52,124,0,
-    106,1,100,0,107,9,114,52,124,0,106,2,114,52,116,3,
-    100,0,107,8,114,38,116,4,130,1,116,3,106,5,124,0,
-    106,1,131,1,124,0,95,0,124,0,106,0,83,0,41,1,
-    78,41,6,114,108,0,0,0,114,103,0,0,0,114,107,0,
-    0,0,218,19,95,98,111,111,116,115,116,114,97,112,95,101,
-    120,116,101,114,110,97,108,218,19,78,111,116,73,109,112,108,
-    101,109,101,110,116,101,100,69,114,114,111,114,90,11,95,103,
-    101,116,95,99,97,99,104,101,100,41,1,114,26,0,0,0,
-    114,10,0,0,0,114,10,0,0,0,114,11,0,0,0,114,
-    112,0,0,0,133,1,0,0,115,12,0,0,0,0,2,10,
-    1,16,1,8,1,4,1,14,1,122,17,77,111,100,117,108,
-    101,83,112,101,99,46,99,97,99,104,101,100,99,2,0,0,
-    0,0,0,0,0,2,0,0,0,2,0,0,0,67,0,0,
-    0,115,10,0,0,0,124,1,124,0,95,0,100,0,83,0,
-    41,1,78,41,1,114,108,0,0,0,41,2,114,26,0,0,
-    0,114,112,0,0,0,114,10,0,0,0,114,10,0,0,0,
-    114,11,0,0,0,114,112,0,0,0,142,1,0,0,115,2,
-    0,0,0,0,2,99,1,0,0,0,0,0,0,0,1,0,
-    0,0,2,0,0,0,67,0,0,0,115,36,0,0,0,124,
-    0,106,0,100,1,107,8,114,26,124,0,106,1,106,2,100,
-    2,131,1,100,3,25,0,83,0,124,0,106,1,83,0,100,
-    1,83,0,41,4,122,32,84,104,101,32,110,97,109,101,32,
-    111,102,32,116,104,101,32,109,111,100,117,108,101,39,115,32,
-    112,97,114,101,110,116,46,78,218,1,46,114,19,0,0,0,
-    41,3,114,106,0,0,0,114,15,0,0,0,218,10,114,112,
-    97,114,116,105,116,105,111,110,41,1,114,26,0,0,0,114,
-    10,0,0,0,114,10,0,0,0,114,11,0,0,0,218,6,
-    112,97,114,101,110,116,146,1,0,0,115,6,0,0,0,0,
-    3,10,1,16,2,122,17,77,111,100,117,108,101,83,112,101,
-    99,46,112,97,114,101,110,116,99,1,0,0,0,0,0,0,
-    0,1,0,0,0,1,0,0,0,67,0,0,0,115,6,0,
-    0,0,124,0,106,0,83,0,41,1,78,41,1,114,107,0,
-    0,0,41,1,114,26,0,0,0,114,10,0,0,0,114,10,
-    0,0,0,114,11,0,0,0,114,113,0,0,0,154,1,0,
-    0,115,2,0,0,0,0,2,122,23,77,111,100,117,108,101,
-    83,112,101,99,46,104,97,115,95,108,111,99,97,116,105,111,
-    110,99,2,0,0,0,0,0,0,0,2,0,0,0,2,0,
-    0,0,67,0,0,0,115,14,0,0,0,116,0,124,1,131,
-    1,124,0,95,1,100,0,83,0,41,1,78,41,2,218,4,
-    98,111,111,108,114,107,0,0,0,41,2,114,26,0,0,0,
-    218,5,118,97,108,117,101,114,10,0,0,0,114,10,0,0,
-    0,114,11,0,0,0,114,113,0,0,0,158,1,0,0,115,
-    2,0,0,0,0,2,41,12,114,1,0,0,0,114,0,0,
-    0,0,114,2,0,0,0,114,3,0,0,0,114,27,0,0,
-    0,114,40,0,0,0,114,114,0,0,0,218,8,112,114,111,
-    112,101,114,116,121,114,112,0,0,0,218,6,115,101,116,116,
-    101,114,114,119,0,0,0,114,113,0,0,0,114,10,0,0,
+    62,122,18,60,109,111,100,117,108,101,32,123,33,114,125,32,
+    40,123,125,41,62,41,5,114,15,0,0,0,114,104,0,0,
+    0,114,94,0,0,0,114,38,0,0,0,114,114,0,0,0,
+    41,2,114,83,0,0,0,114,15,0,0,0,114,10,0,0,
+    0,114,10,0,0,0,114,11,0,0,0,114,92,0,0,0,
+    69,2,0,0,115,16,0,0,0,0,3,20,1,10,1,10,
+    1,10,2,16,2,6,1,14,2,114,92,0,0,0,99,2,
+    0,0,0,0,0,0,0,4,0,0,0,12,0,0,0,67,
+    0,0,0,115,178,0,0,0,124,0,106,0,125,2,116,1,
+    124,2,131,1,143,148,1,0,116,2,106,3,106,4,124,2,
+    131,1,124,1,107,9,114,54,100,1,106,5,124,2,131,1,
+    125,3,116,6,124,3,124,2,100,2,141,2,130,1,124,0,
+    106,7,100,3,107,8,114,106,124,0,106,8,100,3,107,8,
+    114,88,116,6,100,4,124,0,106,0,100,2,141,2,130,1,
+    116,9,124,0,124,1,100,5,100,6,141,3,1,0,124,1,
+    83,0,116,9,124,0,124,1,100,5,100,6,141,3,1,0,
+    116,10,124,0,106,7,100,7,131,2,115,146,124,0,106,7,
+    106,11,124,2,131,1,1,0,110,12,124,0,106,7,106,12,
+    124,1,131,1,1,0,87,0,100,3,81,0,82,0,88,0,
+    116,2,106,3,124,2,25,0,83,0,41,8,122,70,69,120,
+    101,99,117,116,101,32,116,104,101,32,115,112,101,99,39,115,
+    32,115,112,101,99,105,102,105,101,100,32,109,111,100,117,108,
+    101,32,105,110,32,97,110,32,101,120,105,115,116,105,110,103,
+    32,109,111,100,117,108,101,39,115,32,110,97,109,101,115,112,
+    97,99,101,46,122,30,109,111,100,117,108,101,32,123,33,114,
+    125,32,110,111,116,32,105,110,32,115,121,115,46,109,111,100,
+    117,108,101,115,41,1,114,15,0,0,0,78,122,14,109,105,
+    115,115,105,110,103,32,108,111,97,100,101,114,84,41,1,114,
+    130,0,0,0,114,136,0,0,0,41,13,114,15,0,0,0,
+    114,42,0,0,0,114,14,0,0,0,114,80,0,0,0,114,
+    30,0,0,0,114,38,0,0,0,114,71,0,0,0,114,94,
+    0,0,0,114,107,0,0,0,114,134,0,0,0,114,4,0,
+    0,0,218,11,108,111,97,100,95,109,111,100,117,108,101,114,
+    136,0,0,0,41,4,114,83,0,0,0,114,84,0,0,0,
+    114,15,0,0,0,218,3,109,115,103,114,10,0,0,0,114,
+    10,0,0,0,114,11,0,0,0,114,81,0,0,0,86,2,
+    0,0,115,30,0,0,0,0,2,6,1,10,1,16,1,10,
+    1,12,1,10,1,10,1,14,2,14,1,4,1,14,1,12,
+    4,14,2,22,1,114,81,0,0,0,99,1,0,0,0,0,
+    0,0,0,2,0,0,0,27,0,0,0,67,0,0,0,115,
+    206,0,0,0,124,0,106,0,106,1,124,0,106,2,131,1,
+    1,0,116,3,106,4,124,0,106,2,25,0,125,1,116,5,
+    124,1,100,1,100,0,131,3,100,0,107,8,114,76,121,12,
+    124,0,106,0,124,1,95,6,87,0,110,20,4,0,116,7,
+    107,10,114,74,1,0,1,0,1,0,89,0,110,2,88,0,
+    116,5,124,1,100,2,100,0,131,3,100,0,107,8,114,154,
+    121,40,124,1,106,8,124,1,95,9,116,10,124,1,100,3,
+    131,2,115,130,124,0,106,2,106,11,100,4,131,1,100,5,
+    25,0,124,1,95,9,87,0,110,20,4,0,116,7,107,10,
+    114,152,1,0,1,0,1,0,89,0,110,2,88,0,116,5,
+    124,1,100,6,100,0,131,3,100,0,107,8,114,202,121,10,
+    124,0,124,1,95,12,87,0,110,20,4,0,116,7,107,10,
+    114,200,1,0,1,0,1,0,89,0,110,2,88,0,124,1,
+    83,0,41,7,78,114,86,0,0,0,114,131,0,0,0,114,
+    128,0,0,0,114,118,0,0,0,114,19,0,0,0,114,90,
+    0,0,0,41,13,114,94,0,0,0,114,138,0,0,0,114,
+    15,0,0,0,114,14,0,0,0,114,80,0,0,0,114,6,
+    0,0,0,114,86,0,0,0,114,91,0,0,0,114,1,0,
+    0,0,114,131,0,0,0,114,4,0,0,0,114,119,0,0,
+    0,114,90,0,0,0,41,2,114,83,0,0,0,114,84,0,
+    0,0,114,10,0,0,0,114,10,0,0,0,114,11,0,0,
+    0,218,25,95,108,111,97,100,95,98,97,99,107,119,97,114,
+    100,95,99,111,109,112,97,116,105,98,108,101,110,2,0,0,
+    115,40,0,0,0,0,4,14,2,12,1,16,1,2,1,12,
+    1,14,1,6,1,16,1,2,4,8,1,10,1,22,1,14,
+    1,6,1,16,1,2,1,10,1,14,1,6,1,114,140,0,
+    0,0,99,1,0,0,0,0,0,0,0,2,0,0,0,11,
+    0,0,0,67,0,0,0,115,118,0,0,0,124,0,106,0,
+    100,0,107,9,114,30,116,1,124,0,106,0,100,1,131,2,
+    115,30,116,2,124,0,131,1,83,0,116,3,124,0,131,1,
+    125,1,116,4,124,1,131,1,143,54,1,0,124,0,106,0,
+    100,0,107,8,114,84,124,0,106,5,100,0,107,8,114,96,
+    116,6,100,2,124,0,106,7,100,3,141,2,130,1,110,12,
+    124,0,106,0,106,8,124,1,131,1,1,0,87,0,100,0,
+    81,0,82,0,88,0,116,9,106,10,124,0,106,7,25,0,
+    83,0,41,4,78,114,136,0,0,0,122,14,109,105,115,115,
+    105,110,103,32,108,111,97,100,101,114,41,1,114,15,0,0,
+    0,41,11,114,94,0,0,0,114,4,0,0,0,114,140,0,
+    0,0,114,137,0,0,0,114,97,0,0,0,114,107,0,0,
+    0,114,71,0,0,0,114,15,0,0,0,114,136,0,0,0,
+    114,14,0,0,0,114,80,0,0,0,41,2,114,83,0,0,
+    0,114,84,0,0,0,114,10,0,0,0,114,10,0,0,0,
+    114,11,0,0,0,218,14,95,108,111,97,100,95,117,110,108,
+    111,99,107,101,100,139,2,0,0,115,20,0,0,0,0,2,
+    10,2,12,1,8,2,8,1,10,1,10,1,10,1,16,3,
+    22,5,114,141,0,0,0,99,1,0,0,0,0,0,0,0,
+    1,0,0,0,9,0,0,0,67,0,0,0,115,30,0,0,
+    0,116,0,124,0,106,1,131,1,143,10,1,0,116,2,124,
+    0,131,1,83,0,81,0,82,0,88,0,100,1,83,0,41,
+    2,122,191,82,101,116,117,114,110,32,97,32,110,101,119,32,
+    109,111,100,117,108,101,32,111,98,106,101,99,116,44,32,108,
+    111,97,100,101,100,32,98,121,32,116,104,101,32,115,112,101,
+    99,39,115,32,108,111,97,100,101,114,46,10,10,32,32,32,
+    32,84,104,101,32,109,111,100,117,108,101,32,105,115,32,110,
+    111,116,32,97,100,100,101,100,32,116,111,32,105,116,115,32,
+    112,97,114,101,110,116,46,10,10,32,32,32,32,73,102,32,
+    97,32,109,111,100,117,108,101,32,105,115,32,97,108,114,101,
+    97,100,121,32,105,110,32,115,121,115,46,109,111,100,117,108,
+    101,115,44,32,116,104,97,116,32,101,120,105,115,116,105,110,
+    103,32,109,111,100,117,108,101,32,103,101,116,115,10,32,32,
+    32,32,99,108,111,98,98,101,114,101,100,46,10,10,32,32,
+    32,32,78,41,3,114,42,0,0,0,114,15,0,0,0,114,
+    141,0,0,0,41,1,114,83,0,0,0,114,10,0,0,0,
+    114,10,0,0,0,114,11,0,0,0,114,82,0,0,0,162,
+    2,0,0,115,4,0,0,0,0,9,12,1,114,82,0,0,
+    0,99,0,0,0,0,0,0,0,0,0,0,0,0,4,0,
+    0,0,64,0,0,0,115,136,0,0,0,101,0,90,1,100,
+    0,90,2,100,1,90,3,101,4,100,2,100,3,132,0,131,
+    1,90,5,101,6,100,19,100,5,100,6,132,1,131,1,90,
+    7,101,6,100,20,100,7,100,8,132,1,131,1,90,8,101,
+    6,100,9,100,10,132,0,131,1,90,9,101,6,100,11,100,
+    12,132,0,131,1,90,10,101,6,101,11,100,13,100,14,132,
+    0,131,1,131,1,90,12,101,6,101,11,100,15,100,16,132,
+    0,131,1,131,1,90,13,101,6,101,11,100,17,100,18,132,
+    0,131,1,131,1,90,14,101,6,101,15,131,1,90,16,100,
+    4,83,0,41,21,218,15,66,117,105,108,116,105,110,73,109,
+    112,111,114,116,101,114,122,144,77,101,116,97,32,112,97,116,
+    104,32,105,109,112,111,114,116,32,102,111,114,32,98,117,105,
+    108,116,45,105,110,32,109,111,100,117,108,101,115,46,10,10,
+    32,32,32,32,65,108,108,32,109,101,116,104,111,100,115,32,
+    97,114,101,32,101,105,116,104,101,114,32,99,108,97,115,115,
+    32,111,114,32,115,116,97,116,105,99,32,109,101,116,104,111,
+    100,115,32,116,111,32,97,118,111,105,100,32,116,104,101,32,
+    110,101,101,100,32,116,111,10,32,32,32,32,105,110,115,116,
+    97,110,116,105,97,116,101,32,116,104,101,32,99,108,97,115,
+    115,46,10,10,32,32,32,32,99,1,0,0,0,0,0,0,
+    0,1,0,0,0,2,0,0,0,67,0,0,0,115,12,0,
+    0,0,100,1,106,0,124,0,106,1,131,1,83,0,41,2,
+    122,115,82,101,116,117,114,110,32,114,101,112,114,32,102,111,
+    114,32,116,104,101,32,109,111,100,117,108,101,46,10,10,32,
+    32,32,32,32,32,32,32,84,104,101,32,109,101,116,104,111,
+    100,32,105,115,32,100,101,112,114,101,99,97,116,101,100,46,
+    32,32,84,104,101,32,105,109,112,111,114,116,32,109,97,99,
+    104,105,110,101,114,121,32,100,111,101,115,32,116,104,101,32,
+    106,111,98,32,105,116,115,101,108,102,46,10,10,32,32,32,
+    32,32,32,32,32,122,24,60,109,111,100,117,108,101,32,123,
+    33,114,125,32,40,98,117,105,108,116,45,105,110,41,62,41,
+    2,114,38,0,0,0,114,1,0,0,0,41,1,114,84,0,
+    0,0,114,10,0,0,0,114,10,0,0,0,114,11,0,0,
+    0,114,87,0,0,0,186,2,0,0,115,2,0,0,0,0,
+    7,122,27,66,117,105,108,116,105,110,73,109,112,111,114,116,
+    101,114,46,109,111,100,117,108,101,95,114,101,112,114,78,99,
+    4,0,0,0,0,0,0,0,4,0,0,0,5,0,0,0,
+    67,0,0,0,115,44,0,0,0,124,2,100,0,107,9,114,
+    12,100,0,83,0,116,0,106,1,124,1,131,1,114,36,116,
+    2,124,1,124,0,100,1,100,2,141,3,83,0,100,0,83,
+    0,100,0,83,0,41,3,78,122,8,98,117,105,108,116,45,
+    105,110,41,1,114,104,0,0,0,41,3,114,49,0,0,0,
+    90,10,105,115,95,98,117,105,108,116,105,110,114,79,0,0,
+    0,41,4,218,3,99,108,115,114,72,0,0,0,218,4,112,
+    97,116,104,218,6,116,97,114,103,101,116,114,10,0,0,0,
+    114,10,0,0,0,114,11,0,0,0,218,9,102,105,110,100,
+    95,115,112,101,99,195,2,0,0,115,10,0,0,0,0,2,
+    8,1,4,1,10,1,14,2,122,25,66,117,105,108,116,105,
+    110,73,109,112,111,114,116,101,114,46,102,105,110,100,95,115,
+    112,101,99,99,3,0,0,0,0,0,0,0,4,0,0,0,
+    3,0,0,0,67,0,0,0,115,30,0,0,0,124,0,106,
+    0,124,1,124,2,131,2,125,3,124,3,100,1,107,9,114,
+    26,124,3,106,1,83,0,100,1,83,0,41,2,122,175,70,
+    105,110,100,32,116,104,101,32,98,117,105,108,116,45,105,110,
+    32,109,111,100,117,108,101,46,10,10,32,32,32,32,32,32,
+    32,32,73,102,32,39,112,97,116,104,39,32,105,115,32,101,
+    118,101,114,32,115,112,101,99,105,102,105,101,100,32,116,104,
+    101,110,32,116,104,101,32,115,101,97,114,99,104,32,105,115,
+    32,99,111,110,115,105,100,101,114,101,100,32,97,32,102,97,
+    105,108,117,114,101,46,10,10,32,32,32,32,32,32,32,32,
+    84,104,105,115,32,109,101,116,104,111,100,32,105,115,32,100,
+    101,112,114,101,99,97,116,101,100,46,32,32,85,115,101,32,
+    102,105,110,100,95,115,112,101,99,40,41,32,105,110,115,116,
+    101,97,100,46,10,10,32,32,32,32,32,32,32,32,78,41,
+    2,114,146,0,0,0,114,94,0,0,0,41,4,114,143,0,
+    0,0,114,72,0,0,0,114,144,0,0,0,114,83,0,0,
     0,114,10,0,0,0,114,10,0,0,0,114,11,0,0,0,
-    114,102,0,0,0,62,1,0,0,115,20,0,0,0,8,35,
-    4,2,4,1,14,11,8,10,8,12,12,9,14,4,12,8,
-    12,4,114,102,0,0,0,41,2,114,103,0,0,0,114,105,
-    0,0,0,99,2,0,0,0,2,0,0,0,6,0,0,0,
-    14,0,0,0,67,0,0,0,115,154,0,0,0,116,0,124,
-    1,100,1,131,2,114,74,116,1,100,2,107,8,114,22,116,
-    2,130,1,116,1,106,3,125,4,124,3,100,2,107,8,114,
-    48,124,4,124,0,124,1,100,3,141,2,83,0,124,3,114,
-    56,103,0,110,2,100,2,125,5,124,4,124,0,124,1,124,
-    5,100,4,141,3,83,0,124,3,100,2,107,8,114,138,116,
-    0,124,1,100,5,131,2,114,134,121,14,124,1,106,4,124,
-    0,131,1,125,3,87,0,113,138,4,0,116,5,107,10,114,
-    130,1,0,1,0,1,0,100,2,125,3,89,0,113,138,88,
-    0,110,4,100,6,125,3,116,6,124,0,124,1,124,2,124,
-    3,100,7,141,4,83,0,41,8,122,53,82,101,116,117,114,
-    110,32,97,32,109,111,100,117,108,101,32,115,112,101,99,32,
-    98,97,115,101,100,32,111,110,32,118,97,114,105,111,117,115,
-    32,108,111,97,100,101,114,32,109,101,116,104,111,100,115,46,
-    90,12,103,101,116,95,102,105,108,101,110,97,109,101,78,41,
-    1,114,93,0,0,0,41,2,114,93,0,0,0,114,106,0,
-    0,0,114,105,0,0,0,70,41,2,114,103,0,0,0,114,
-    105,0,0,0,41,7,114,4,0,0,0,114,115,0,0,0,
-    114,116,0,0,0,218,23,115,112,101,99,95,102,114,111,109,
-    95,102,105,108,101,95,108,111,99,97,116,105,111,110,114,105,
-    0,0,0,114,70,0,0,0,114,102,0,0,0,41,6,114,
-    15,0,0,0,114,93,0,0,0,114,103,0,0,0,114,105,
-    0,0,0,114,124,0,0,0,90,6,115,101,97,114,99,104,
-    114,10,0,0,0,114,10,0,0,0,114,11,0,0,0,114,
-    78,0,0,0,163,1,0,0,115,34,0,0,0,0,2,10,
-    1,8,1,4,1,6,2,8,1,12,1,12,1,6,1,8,
-    2,8,1,10,1,2,1,14,1,14,1,12,3,4,2,114,
-    78,0,0,0,99,3,0,0,0,0,0,0,0,8,0,0,
-    0,53,0,0,0,67,0,0,0,115,56,1,0,0,121,10,
-    124,0,106,0,125,3,87,0,110,20,4,0,116,1,107,10,
-    114,30,1,0,1,0,1,0,89,0,110,14,88,0,124,3,
-    100,0,107,9,114,44,124,3,83,0,124,0,106,2,125,4,
-    124,1,100,0,107,8,114,90,121,10,124,0,106,3,125,1,
-    87,0,110,20,4,0,116,1,107,10,114,88,1,0,1,0,
-    1,0,89,0,110,2,88,0,121,10,124,0,106,4,125,5,
-    87,0,110,24,4,0,116,1,107,10,114,124,1,0,1,0,
-    1,0,100,0,125,5,89,0,110,2,88,0,124,2,100,0,
-    107,8,114,184,124,5,100,0,107,8,114,180,121,10,124,1,
-    106,5,125,2,87,0,113,184,4,0,116,1,107,10,114,176,
-    1,0,1,0,1,0,100,0,125,2,89,0,113,184,88,0,
-    110,4,124,5,125,2,121,10,124,0,106,6,125,6,87,0,
-    110,24,4,0,116,1,107,10,114,218,1,0,1,0,1,0,
-    100,0,125,6,89,0,110,2,88,0,121,14,116,7,124,0,
-    106,8,131,1,125,7,87,0,110,26,4,0,116,1,107,10,
-    144,1,114,4,1,0,1,0,1,0,100,0,125,7,89,0,
-    110,2,88,0,116,9,124,4,124,1,124,2,100,1,141,3,
-    125,3,124,5,100,0,107,8,144,1,114,34,100,2,110,2,
-    100,3,124,3,95,10,124,6,124,3,95,11,124,7,124,3,
-    95,12,124,3,83,0,41,4,78,41,1,114,103,0,0,0,
-    70,84,41,13,114,89,0,0,0,114,90,0,0,0,114,1,
-    0,0,0,114,85,0,0,0,114,92,0,0,0,90,7,95,
-    79,82,73,71,73,78,218,10,95,95,99,97,99,104,101,100,
-    95,95,218,4,108,105,115,116,218,8,95,95,112,97,116,104,
-    95,95,114,102,0,0,0,114,107,0,0,0,114,112,0,0,
-    0,114,106,0,0,0,41,8,114,83,0,0,0,114,93,0,
-    0,0,114,103,0,0,0,114,82,0,0,0,114,15,0,0,
-    0,90,8,108,111,99,97,116,105,111,110,114,112,0,0,0,
-    114,106,0,0,0,114,10,0,0,0,114,10,0,0,0,114,
-    11,0,0,0,218,17,95,115,112,101,99,95,102,114,111,109,
-    95,109,111,100,117,108,101,192,1,0,0,115,72,0,0,0,
-    0,2,2,1,10,1,14,1,6,2,8,1,4,2,6,1,
-    8,1,2,1,10,1,14,2,6,1,2,1,10,1,14,1,
-    10,1,8,1,8,1,2,1,10,1,14,1,12,2,4,1,
-    2,1,10,1,14,1,10,1,2,1,14,1,16,1,10,2,
-    14,1,20,1,6,1,6,1,114,128,0,0,0,70,41,1,
-    218,8,111,118,101,114,114,105,100,101,99,2,0,0,0,1,
-    0,0,0,5,0,0,0,59,0,0,0,67,0,0,0,115,
-    212,1,0,0,124,2,115,20,116,0,124,1,100,1,100,0,
-    131,3,100,0,107,8,114,54,121,12,124,0,106,1,124,1,
-    95,2,87,0,110,20,4,0,116,3,107,10,114,52,1,0,
-    1,0,1,0,89,0,110,2,88,0,124,2,115,74,116,0,
-    124,1,100,2,100,0,131,3,100,0,107,8,114,166,124,0,
-    106,4,125,3,124,3,100,0,107,8,114,134,124,0,106,5,
-    100,0,107,9,114,134,116,6,100,0,107,8,114,110,116,7,
-    130,1,116,6,106,8,125,4,124,4,106,9,124,4,131,1,
-    125,3,124,0,106,5,124,3,95,10,121,10,124,3,124,1,
-    95,11,87,0,110,20,4,0,116,3,107,10,114,164,1,0,
-    1,0,1,0,89,0,110,2,88,0,124,2,115,186,116,0,
-    124,1,100,3,100,0,131,3,100,0,107,8,114,220,121,12,
-    124,0,106,12,124,1,95,13,87,0,110,20,4,0,116,3,
-    107,10,114,218,1,0,1,0,1,0,89,0,110,2,88,0,
-    121,10,124,0,124,1,95,14,87,0,110,20,4,0,116,3,
-    107,10,114,250,1,0,1,0,1,0,89,0,110,2,88,0,
-    124,2,144,1,115,20,116,0,124,1,100,4,100,0,131,3,
-    100,0,107,8,144,1,114,68,124,0,106,5,100,0,107,9,
-    144,1,114,68,121,12,124,0,106,5,124,1,95,15,87,0,
-    110,22,4,0,116,3,107,10,144,1,114,66,1,0,1,0,
-    1,0,89,0,110,2,88,0,124,0,106,16,144,1,114,208,
-    124,2,144,1,115,100,116,0,124,1,100,5,100,0,131,3,
-    100,0,107,8,144,1,114,136,121,12,124,0,106,17,124,1,
-    95,18,87,0,110,22,4,0,116,3,107,10,144,1,114,134,
-    1,0,1,0,1,0,89,0,110,2,88,0,124,2,144,1,
-    115,160,116,0,124,1,100,6,100,0,131,3,100,0,107,8,
-    144,1,114,208,124,0,106,19,100,0,107,9,144,1,114,208,
-    121,12,124,0,106,19,124,1,95,20,87,0,110,22,4,0,
-    116,3,107,10,144,1,114,206,1,0,1,0,1,0,89,0,
-    110,2,88,0,124,1,83,0,41,7,78,114,1,0,0,0,
-    114,85,0,0,0,218,11,95,95,112,97,99,107,97,103,101,
-    95,95,114,127,0,0,0,114,92,0,0,0,114,125,0,0,
-    0,41,21,114,6,0,0,0,114,15,0,0,0,114,1,0,
-    0,0,114,90,0,0,0,114,93,0,0,0,114,106,0,0,
-    0,114,115,0,0,0,114,116,0,0,0,218,16,95,78,97,
-    109,101,115,112,97,99,101,76,111,97,100,101,114,218,7,95,
-    95,110,101,119,95,95,90,5,95,112,97,116,104,114,85,0,
-    0,0,114,119,0,0,0,114,130,0,0,0,114,89,0,0,
-    0,114,127,0,0,0,114,113,0,0,0,114,103,0,0,0,
-    114,92,0,0,0,114,112,0,0,0,114,125,0,0,0,41,
-    5,114,82,0,0,0,114,83,0,0,0,114,129,0,0,0,
-    114,93,0,0,0,114,131,0,0,0,114,10,0,0,0,114,
-    10,0,0,0,114,11,0,0,0,218,18,95,105,110,105,116,
-    95,109,111,100,117,108,101,95,97,116,116,114,115,237,1,0,
-    0,115,92,0,0,0,0,4,20,1,2,1,12,1,14,1,
-    6,2,20,1,6,1,8,2,10,1,8,1,4,1,6,2,
-    10,1,8,1,2,1,10,1,14,1,6,2,20,1,2,1,
-    12,1,14,1,6,2,2,1,10,1,14,1,6,2,24,1,
-    12,1,2,1,12,1,16,1,6,2,8,1,24,1,2,1,
-    12,1,16,1,6,2,24,1,12,1,2,1,12,1,16,1,
-    6,1,114,133,0,0,0,99,1,0,0,0,0,0,0,0,
-    2,0,0,0,3,0,0,0,67,0,0,0,115,82,0,0,
-    0,100,1,125,1,116,0,124,0,106,1,100,2,131,2,114,
-    30,124,0,106,1,106,2,124,0,131,1,125,1,110,20,116,
-    0,124,0,106,1,100,3,131,2,114,50,116,3,100,4,131,
-    1,130,1,124,1,100,1,107,8,114,68,116,4,124,0,106,
-    5,131,1,125,1,116,6,124,0,124,1,131,2,1,0,124,
-    1,83,0,41,5,122,43,67,114,101,97,116,101,32,97,32,
-    109,111,100,117,108,101,32,98,97,115,101,100,32,111,110,32,
-    116,104,101,32,112,114,111,118,105,100,101,100,32,115,112,101,
-    99,46,78,218,13,99,114,101,97,116,101,95,109,111,100,117,
-    108,101,218,11,101,120,101,99,95,109,111,100,117,108,101,122,
-    66,108,111,97,100,101,114,115,32,116,104,97,116,32,100,101,
-    102,105,110,101,32,101,120,101,99,95,109,111,100,117,108,101,
-    40,41,32,109,117,115,116,32,97,108,115,111,32,100,101,102,
-    105,110,101,32,99,114,101,97,116,101,95,109,111,100,117,108,
-    101,40,41,41,7,114,4,0,0,0,114,93,0,0,0,114,
-    134,0,0,0,114,70,0,0,0,114,16,0,0,0,114,15,
-    0,0,0,114,133,0,0,0,41,2,114,82,0,0,0,114,
-    83,0,0,0,114,10,0,0,0,114,10,0,0,0,114,11,
-    0,0,0,218,16,109,111,100,117,108,101,95,102,114,111,109,
-    95,115,112,101,99,41,2,0,0,115,18,0,0,0,0,3,
-    4,1,12,3,14,1,12,1,8,2,8,1,10,1,10,1,
-    114,136,0,0,0,99,1,0,0,0,0,0,0,0,2,0,
-    0,0,3,0,0,0,67,0,0,0,115,106,0,0,0,124,
-    0,106,0,100,1,107,8,114,14,100,2,110,4,124,0,106,
-    0,125,1,124,0,106,1,100,1,107,8,114,66,124,0,106,
-    2,100,1,107,8,114,50,100,3,106,3,124,1,131,1,83,
-    0,100,4,106,3,124,1,124,0,106,2,131,2,83,0,110,
-    36,124,0,106,4,114,86,100,5,106,3,124,1,124,0,106,
-    1,131,2,83,0,100,6,106,3,124,0,106,0,124,0,106,
-    1,131,2,83,0,100,1,83,0,41,7,122,38,82,101,116,
-    117,114,110,32,116,104,101,32,114,101,112,114,32,116,111,32,
-    117,115,101,32,102,111,114,32,116,104,101,32,109,111,100,117,
-    108,101,46,78,114,87,0,0,0,122,13,60,109,111,100,117,
-    108,101,32,123,33,114,125,62,122,20,60,109,111,100,117,108,
-    101,32,123,33,114,125,32,40,123,33,114,125,41,62,122,23,
-    60,109,111,100,117,108,101,32,123,33,114,125,32,102,114,111,
-    109,32,123,33,114,125,62,122,18,60,109,111,100,117,108,101,
-    32,123,33,114,125,32,40,123,125,41,62,41,5,114,15,0,
-    0,0,114,103,0,0,0,114,93,0,0,0,114,38,0,0,
-    0,114,113,0,0,0,41,2,114,82,0,0,0,114,15,0,
+    218,11,102,105,110,100,95,109,111,100,117,108,101,204,2,0,
+    0,115,4,0,0,0,0,9,12,1,122,27,66,117,105,108,
+    116,105,110,73,109,112,111,114,116,101,114,46,102,105,110,100,
+    95,109,111,100,117,108,101,99,2,0,0,0,0,0,0,0,
+    2,0,0,0,4,0,0,0,67,0,0,0,115,46,0,0,
+    0,124,1,106,0,116,1,106,2,107,7,114,34,116,3,100,
+    1,106,4,124,1,106,0,131,1,124,1,106,0,100,2,141,
+    2,130,1,116,5,116,6,106,7,124,1,131,2,83,0,41,
+    3,122,24,67,114,101,97,116,101,32,97,32,98,117,105,108,
+    116,45,105,110,32,109,111,100,117,108,101,122,29,123,33,114,
+    125,32,105,115,32,110,111,116,32,97,32,98,117,105,108,116,
+    45,105,110,32,109,111,100,117,108,101,41,1,114,15,0,0,
+    0,41,8,114,15,0,0,0,114,14,0,0,0,114,70,0,
+    0,0,114,71,0,0,0,114,38,0,0,0,114,59,0,0,
+    0,114,49,0,0,0,90,14,99,114,101,97,116,101,95,98,
+    117,105,108,116,105,110,41,2,114,26,0,0,0,114,83,0,
     0,0,114,10,0,0,0,114,10,0,0,0,114,11,0,0,
-    0,114,91,0,0,0,58,2,0,0,115,16,0,0,0,0,
-    3,20,1,10,1,10,1,10,2,16,2,6,1,14,2,114,
-    91,0,0,0,99,2,0,0,0,0,0,0,0,4,0,0,
-    0,12,0,0,0,67,0,0,0,115,186,0,0,0,124,0,
-    106,0,125,2,116,1,106,2,131,0,1,0,116,3,124,2,
-    131,1,143,148,1,0,116,4,106,5,106,6,124,2,131,1,
-    124,1,107,9,114,62,100,1,106,7,124,2,131,1,125,3,
-    116,8,124,3,124,2,100,2,141,2,130,1,124,0,106,9,
-    100,3,107,8,114,114,124,0,106,10,100,3,107,8,114,96,
-    116,8,100,4,124,0,106,0,100,2,141,2,130,1,116,11,
-    124,0,124,1,100,5,100,6,141,3,1,0,124,1,83,0,
-    116,11,124,0,124,1,100,5,100,6,141,3,1,0,116,12,
-    124,0,106,9,100,7,131,2,115,154,124,0,106,9,106,13,
-    124,2,131,1,1,0,110,12,124,0,106,9,106,14,124,1,
-    131,1,1,0,87,0,100,3,81,0,82,0,88,0,116,4,
-    106,5,124,2,25,0,83,0,41,8,122,70,69,120,101,99,
-    117,116,101,32,116,104,101,32,115,112,101,99,39,115,32,115,
-    112,101,99,105,102,105,101,100,32,109,111,100,117,108,101,32,
-    105,110,32,97,110,32,101,120,105,115,116,105,110,103,32,109,
-    111,100,117,108,101,39,115,32,110,97,109,101,115,112,97,99,
-    101,46,122,30,109,111,100,117,108,101,32,123,33,114,125,32,
-    110,111,116,32,105,110,32,115,121,115,46,109,111,100,117,108,
-    101,115,41,1,114,15,0,0,0,78,122,14,109,105,115,115,
-    105,110,103,32,108,111,97,100,101,114,84,41,1,114,129,0,
-    0,0,114,135,0,0,0,41,15,114,15,0,0,0,114,46,
-    0,0,0,218,12,97,99,113,117,105,114,101,95,108,111,99,
-    107,114,42,0,0,0,114,14,0,0,0,114,79,0,0,0,
-    114,30,0,0,0,114,38,0,0,0,114,70,0,0,0,114,
-    93,0,0,0,114,106,0,0,0,114,133,0,0,0,114,4,
-    0,0,0,218,11,108,111,97,100,95,109,111,100,117,108,101,
-    114,135,0,0,0,41,4,114,82,0,0,0,114,83,0,0,
-    0,114,15,0,0,0,218,3,109,115,103,114,10,0,0,0,
-    114,10,0,0,0,114,11,0,0,0,114,80,0,0,0,75,
-    2,0,0,115,32,0,0,0,0,2,6,1,8,1,10,1,
-    16,1,10,1,12,1,10,1,10,1,14,2,14,1,4,1,
-    14,1,12,4,14,2,22,1,114,80,0,0,0,99,1,0,
-    0,0,0,0,0,0,2,0,0,0,27,0,0,0,67,0,
-    0,0,115,206,0,0,0,124,0,106,0,106,1,124,0,106,
-    2,131,1,1,0,116,3,106,4,124,0,106,2,25,0,125,
-    1,116,5,124,1,100,1,100,0,131,3,100,0,107,8,114,
-    76,121,12,124,0,106,0,124,1,95,6,87,0,110,20,4,
-    0,116,7,107,10,114,74,1,0,1,0,1,0,89,0,110,
-    2,88,0,116,5,124,1,100,2,100,0,131,3,100,0,107,
-    8,114,154,121,40,124,1,106,8,124,1,95,9,116,10,124,
-    1,100,3,131,2,115,130,124,0,106,2,106,11,100,4,131,
-    1,100,5,25,0,124,1,95,9,87,0,110,20,4,0,116,
-    7,107,10,114,152,1,0,1,0,1,0,89,0,110,2,88,
-    0,116,5,124,1,100,6,100,0,131,3,100,0,107,8,114,
-    202,121,10,124,0,124,1,95,12,87,0,110,20,4,0,116,
-    7,107,10,114,200,1,0,1,0,1,0,89,0,110,2,88,
-    0,124,1,83,0,41,7,78,114,85,0,0,0,114,130,0,
-    0,0,114,127,0,0,0,114,117,0,0,0,114,19,0,0,
-    0,114,89,0,0,0,41,13,114,93,0,0,0,114,138,0,
-    0,0,114,15,0,0,0,114,14,0,0,0,114,79,0,0,
-    0,114,6,0,0,0,114,85,0,0,0,114,90,0,0,0,
-    114,1,0,0,0,114,130,0,0,0,114,4,0,0,0,114,
-    118,0,0,0,114,89,0,0,0,41,2,114,82,0,0,0,
-    114,83,0,0,0,114,10,0,0,0,114,10,0,0,0,114,
-    11,0,0,0,218,25,95,108,111,97,100,95,98,97,99,107,
-    119,97,114,100,95,99,111,109,112,97,116,105,98,108,101,100,
-    2,0,0,115,40,0,0,0,0,4,14,2,12,1,16,1,
-    2,1,12,1,14,1,6,1,16,1,2,4,8,1,10,1,
-    22,1,14,1,6,1,16,1,2,1,10,1,14,1,6,1,
-    114,140,0,0,0,99,1,0,0,0,0,0,0,0,2,0,
-    0,0,11,0,0,0,67,0,0,0,115,118,0,0,0,124,
-    0,106,0,100,0,107,9,114,30,116,1,124,0,106,0,100,
-    1,131,2,115,30,116,2,124,0,131,1,83,0,116,3,124,
-    0,131,1,125,1,116,4,124,1,131,1,143,54,1,0,124,
-    0,106,0,100,0,107,8,114,84,124,0,106,5,100,0,107,
-    8,114,96,116,6,100,2,124,0,106,7,100,3,141,2,130,
-    1,110,12,124,0,106,0,106,8,124,1,131,1,1,0,87,
-    0,100,0,81,0,82,0,88,0,116,9,106,10,124,0,106,
-    7,25,0,83,0,41,4,78,114,135,0,0,0,122,14,109,
-    105,115,115,105,110,103,32,108,111,97,100,101,114,41,1,114,
-    15,0,0,0,41,11,114,93,0,0,0,114,4,0,0,0,
-    114,140,0,0,0,114,136,0,0,0,114,96,0,0,0,114,
-    106,0,0,0,114,70,0,0,0,114,15,0,0,0,114,135,
-    0,0,0,114,14,0,0,0,114,79,0,0,0,41,2,114,
-    82,0,0,0,114,83,0,0,0,114,10,0,0,0,114,10,
-    0,0,0,114,11,0,0,0,218,14,95,108,111,97,100,95,
-    117,110,108,111,99,107,101,100,129,2,0,0,115,20,0,0,
-    0,0,2,10,2,12,1,8,2,8,1,10,1,10,1,10,
-    1,16,3,22,5,114,141,0,0,0,99,1,0,0,0,0,
-    0,0,0,1,0,0,0,9,0,0,0,67,0,0,0,115,
-    38,0,0,0,116,0,106,1,131,0,1,0,116,2,124,0,
-    106,3,131,1,143,10,1,0,116,4,124,0,131,1,83,0,
-    81,0,82,0,88,0,100,1,83,0,41,2,122,191,82,101,
-    116,117,114,110,32,97,32,110,101,119,32,109,111,100,117,108,
-    101,32,111,98,106,101,99,116,44,32,108,111,97,100,101,100,
-    32,98,121,32,116,104,101,32,115,112,101,99,39,115,32,108,
-    111,97,100,101,114,46,10,10,32,32,32,32,84,104,101,32,
-    109,111,100,117,108,101,32,105,115,32,110,111,116,32,97,100,
-    100,101,100,32,116,111,32,105,116,115,32,112,97,114,101,110,
-    116,46,10,10,32,32,32,32,73,102,32,97,32,109,111,100,
-    117,108,101,32,105,115,32,97,108,114,101,97,100,121,32,105,
-    110,32,115,121,115,46,109,111,100,117,108,101,115,44,32,116,
-    104,97,116,32,101,120,105,115,116,105,110,103,32,109,111,100,
-    117,108,101,32,103,101,116,115,10,32,32,32,32,99,108,111,
-    98,98,101,114,101,100,46,10,10,32,32,32,32,78,41,5,
-    114,46,0,0,0,114,137,0,0,0,114,42,0,0,0,114,
-    15,0,0,0,114,141,0,0,0,41,1,114,82,0,0,0,
-    114,10,0,0,0,114,10,0,0,0,114,11,0,0,0,114,
-    81,0,0,0,152,2,0,0,115,6,0,0,0,0,9,8,
-    1,12,1,114,81,0,0,0,99,0,0,0,0,0,0,0,
-    0,0,0,0,0,4,0,0,0,64,0,0,0,115,136,0,
-    0,0,101,0,90,1,100,0,90,2,100,1,90,3,101,4,
-    100,2,100,3,132,0,131,1,90,5,101,6,100,19,100,5,
-    100,6,132,1,131,1,90,7,101,6,100,20,100,7,100,8,
-    132,1,131,1,90,8,101,6,100,9,100,10,132,0,131,1,
-    90,9,101,6,100,11,100,12,132,0,131,1,90,10,101,6,
-    101,11,100,13,100,14,132,0,131,1,131,1,90,12,101,6,
-    101,11,100,15,100,16,132,0,131,1,131,1,90,13,101,6,
-    101,11,100,17,100,18,132,0,131,1,131,1,90,14,101,6,
-    101,15,131,1,90,16,100,4,83,0,41,21,218,15,66,117,
-    105,108,116,105,110,73,109,112,111,114,116,101,114,122,144,77,
-    101,116,97,32,112,97,116,104,32,105,109,112,111,114,116,32,
-    102,111,114,32,98,117,105,108,116,45,105,110,32,109,111,100,
-    117,108,101,115,46,10,10,32,32,32,32,65,108,108,32,109,
-    101,116,104,111,100,115,32,97,114,101,32,101,105,116,104,101,
-    114,32,99,108,97,115,115,32,111,114,32,115,116,97,116,105,
-    99,32,109,101,116,104,111,100,115,32,116,111,32,97,118,111,
-    105,100,32,116,104,101,32,110,101,101,100,32,116,111,10,32,
-    32,32,32,105,110,115,116,97,110,116,105,97,116,101,32,116,
-    104,101,32,99,108,97,115,115,46,10,10,32,32,32,32,99,
-    1,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,
-    67,0,0,0,115,12,0,0,0,100,1,106,0,124,0,106,
-    1,131,1,83,0,41,2,122,115,82,101,116,117,114,110,32,
-    114,101,112,114,32,102,111,114,32,116,104,101,32,109,111,100,
-    117,108,101,46,10,10,32,32,32,32,32,32,32,32,84,104,
-    101,32,109,101,116,104,111,100,32,105,115,32,100,101,112,114,
-    101,99,97,116,101,100,46,32,32,84,104,101,32,105,109,112,
-    111,114,116,32,109,97,99,104,105,110,101,114,121,32,100,111,
-    101,115,32,116,104,101,32,106,111,98,32,105,116,115,101,108,
-    102,46,10,10,32,32,32,32,32,32,32,32,122,24,60,109,
-    111,100,117,108,101,32,123,33,114,125,32,40,98,117,105,108,
-    116,45,105,110,41,62,41,2,114,38,0,0,0,114,1,0,
-    0,0,41,1,114,83,0,0,0,114,10,0,0,0,114,10,
-    0,0,0,114,11,0,0,0,114,86,0,0,0,177,2,0,
-    0,115,2,0,0,0,0,7,122,27,66,117,105,108,116,105,
-    110,73,109,112,111,114,116,101,114,46,109,111,100,117,108,101,
-    95,114,101,112,114,78,99,4,0,0,0,0,0,0,0,4,
-    0,0,0,5,0,0,0,67,0,0,0,115,44,0,0,0,
-    124,2,100,0,107,9,114,12,100,0,83,0,116,0,106,1,
-    124,1,131,1,114,36,116,2,124,1,124,0,100,1,100,2,
-    141,3,83,0,100,0,83,0,100,0,83,0,41,3,78,122,
-    8,98,117,105,108,116,45,105,110,41,1,114,103,0,0,0,
-    41,3,114,46,0,0,0,90,10,105,115,95,98,117,105,108,
-    116,105,110,114,78,0,0,0,41,4,218,3,99,108,115,114,
-    71,0,0,0,218,4,112,97,116,104,218,6,116,97,114,103,
-    101,116,114,10,0,0,0,114,10,0,0,0,114,11,0,0,
-    0,218,9,102,105,110,100,95,115,112,101,99,186,2,0,0,
-    115,10,0,0,0,0,2,8,1,4,1,10,1,14,2,122,
-    25,66,117,105,108,116,105,110,73,109,112,111,114,116,101,114,
-    46,102,105,110,100,95,115,112,101,99,99,3,0,0,0,0,
-    0,0,0,4,0,0,0,3,0,0,0,67,0,0,0,115,
-    30,0,0,0,124,0,106,0,124,1,124,2,131,2,125,3,
-    124,3,100,1,107,9,114,26,124,3,106,1,83,0,100,1,
-    83,0,41,2,122,175,70,105,110,100,32,116,104,101,32,98,
-    117,105,108,116,45,105,110,32,109,111,100,117,108,101,46,10,
-    10,32,32,32,32,32,32,32,32,73,102,32,39,112,97,116,
-    104,39,32,105,115,32,101,118,101,114,32,115,112,101,99,105,
-    102,105,101,100,32,116,104,101,110,32,116,104,101,32,115,101,
-    97,114,99,104,32,105,115,32,99,111,110,115,105,100,101,114,
-    101,100,32,97,32,102,97,105,108,117,114,101,46,10,10,32,
-    32,32,32,32,32,32,32,84,104,105,115,32,109,101,116,104,
-    111,100,32,105,115,32,100,101,112,114,101,99,97,116,101,100,
-    46,32,32,85,115,101,32,102,105,110,100,95,115,112,101,99,
-    40,41,32,105,110,115,116,101,97,100,46,10,10,32,32,32,
-    32,32,32,32,32,78,41,2,114,146,0,0,0,114,93,0,
-    0,0,41,4,114,143,0,0,0,114,71,0,0,0,114,144,
-    0,0,0,114,82,0,0,0,114,10,0,0,0,114,10,0,
-    0,0,114,11,0,0,0,218,11,102,105,110,100,95,109,111,
-    100,117,108,101,195,2,0,0,115,4,0,0,0,0,9,12,
-    1,122,27,66,117,105,108,116,105,110,73,109,112,111,114,116,
-    101,114,46,102,105,110,100,95,109,111,100,117,108,101,99,2,
-    0,0,0,0,0,0,0,2,0,0,0,4,0,0,0,67,
-    0,0,0,115,46,0,0,0,124,1,106,0,116,1,106,2,
-    107,7,114,34,116,3,100,1,106,4,124,1,106,0,131,1,
-    124,1,106,0,100,2,141,2,130,1,116,5,116,6,106,7,
-    124,1,131,2,83,0,41,3,122,24,67,114,101,97,116,101,
-    32,97,32,98,117,105,108,116,45,105,110,32,109,111,100,117,
-    108,101,122,29,123,33,114,125,32,105,115,32,110,111,116,32,
-    97,32,98,117,105,108,116,45,105,110,32,109,111,100,117,108,
-    101,41,1,114,15,0,0,0,41,8,114,15,0,0,0,114,
-    14,0,0,0,114,69,0,0,0,114,70,0,0,0,114,38,
-    0,0,0,114,58,0,0,0,114,46,0,0,0,90,14,99,
-    114,101,97,116,101,95,98,117,105,108,116,105,110,41,2,114,
-    26,0,0,0,114,82,0,0,0,114,10,0,0,0,114,10,
-    0,0,0,114,11,0,0,0,114,134,0,0,0,207,2,0,
-    0,115,8,0,0,0,0,3,12,1,12,1,10,1,122,29,
-    66,117,105,108,116,105,110,73,109,112,111,114,116,101,114,46,
-    99,114,101,97,116,101,95,109,111,100,117,108,101,99,2,0,
-    0,0,0,0,0,0,2,0,0,0,3,0,0,0,67,0,
-    0,0,115,16,0,0,0,116,0,116,1,106,2,124,1,131,
-    2,1,0,100,1,83,0,41,2,122,22,69,120,101,99,32,
-    97,32,98,117,105,108,116,45,105,110,32,109,111,100,117,108,
-    101,78,41,3,114,58,0,0,0,114,46,0,0,0,90,12,
-    101,120,101,99,95,98,117,105,108,116,105,110,41,2,114,26,
-    0,0,0,114,83,0,0,0,114,10,0,0,0,114,10,0,
-    0,0,114,11,0,0,0,114,135,0,0,0,215,2,0,0,
-    115,2,0,0,0,0,3,122,27,66,117,105,108,116,105,110,
-    73,109,112,111,114,116,101,114,46,101,120,101,99,95,109,111,
-    100,117,108,101,99,2,0,0,0,0,0,0,0,2,0,0,
-    0,1,0,0,0,67,0,0,0,115,4,0,0,0,100,1,
-    83,0,41,2,122,57,82,101,116,117,114,110,32,78,111,110,
-    101,32,97,115,32,98,117,105,108,116,45,105,110,32,109,111,
-    100,117,108,101,115,32,100,111,32,110,111,116,32,104,97,118,
-    101,32,99,111,100,101,32,111,98,106,101,99,116,115,46,78,
-    114,10,0,0,0,41,2,114,143,0,0,0,114,71,0,0,
+    0,114,135,0,0,0,216,2,0,0,115,8,0,0,0,0,
+    3,12,1,12,1,10,1,122,29,66,117,105,108,116,105,110,
+    73,109,112,111,114,116,101,114,46,99,114,101,97,116,101,95,
+    109,111,100,117,108,101,99,2,0,0,0,0,0,0,0,2,
+    0,0,0,3,0,0,0,67,0,0,0,115,16,0,0,0,
+    116,0,116,1,106,2,124,1,131,2,1,0,100,1,83,0,
+    41,2,122,22,69,120,101,99,32,97,32,98,117,105,108,116,
+    45,105,110,32,109,111,100,117,108,101,78,41,3,114,59,0,
+    0,0,114,49,0,0,0,90,12,101,120,101,99,95,98,117,
+    105,108,116,105,110,41,2,114,26,0,0,0,114,84,0,0,
     0,114,10,0,0,0,114,10,0,0,0,114,11,0,0,0,
-    218,8,103,101,116,95,99,111,100,101,220,2,0,0,115,2,
-    0,0,0,0,4,122,24,66,117,105,108,116,105,110,73,109,
-    112,111,114,116,101,114,46,103,101,116,95,99,111,100,101,99,
-    2,0,0,0,0,0,0,0,2,0,0,0,1,0,0,0,
-    67,0,0,0,115,4,0,0,0,100,1,83,0,41,2,122,
-    56,82,101,116,117,114,110,32,78,111,110,101,32,97,115,32,
-    98,117,105,108,116,45,105,110,32,109,111,100,117,108,101,115,
-    32,100,111,32,110,111,116,32,104,97,118,101,32,115,111,117,
-    114,99,101,32,99,111,100,101,46,78,114,10,0,0,0,41,
-    2,114,143,0,0,0,114,71,0,0,0,114,10,0,0,0,
-    114,10,0,0,0,114,11,0,0,0,218,10,103,101,116,95,
-    115,111,117,114,99,101,226,2,0,0,115,2,0,0,0,0,
-    4,122,26,66,117,105,108,116,105,110,73,109,112,111,114,116,
-    101,114,46,103,101,116,95,115,111,117,114,99,101,99,2,0,
+    114,136,0,0,0,224,2,0,0,115,2,0,0,0,0,3,
+    122,27,66,117,105,108,116,105,110,73,109,112,111,114,116,101,
+    114,46,101,120,101,99,95,109,111,100,117,108,101,99,2,0,
     0,0,0,0,0,0,2,0,0,0,1,0,0,0,67,0,
-    0,0,115,4,0,0,0,100,1,83,0,41,2,122,52,82,
-    101,116,117,114,110,32,70,97,108,115,101,32,97,115,32,98,
-    117,105,108,116,45,105,110,32,109,111,100,117,108,101,115,32,
-    97,114,101,32,110,101,118,101,114,32,112,97,99,107,97,103,
-    101,115,46,70,114,10,0,0,0,41,2,114,143,0,0,0,
-    114,71,0,0,0,114,10,0,0,0,114,10,0,0,0,114,
-    11,0,0,0,114,105,0,0,0,232,2,0,0,115,2,0,
-    0,0,0,4,122,26,66,117,105,108,116,105,110,73,109,112,
-    111,114,116,101,114,46,105,115,95,112,97,99,107,97,103,101,
-    41,2,78,78,41,1,78,41,17,114,1,0,0,0,114,0,
-    0,0,0,114,2,0,0,0,114,3,0,0,0,218,12,115,
-    116,97,116,105,99,109,101,116,104,111,100,114,86,0,0,0,
-    218,11,99,108,97,115,115,109,101,116,104,111,100,114,146,0,
-    0,0,114,147,0,0,0,114,134,0,0,0,114,135,0,0,
-    0,114,74,0,0,0,114,148,0,0,0,114,149,0,0,0,
-    114,105,0,0,0,114,84,0,0,0,114,138,0,0,0,114,
-    10,0,0,0,114,10,0,0,0,114,10,0,0,0,114,11,
-    0,0,0,114,142,0,0,0,168,2,0,0,115,30,0,0,
-    0,8,7,4,2,12,9,2,1,12,8,2,1,12,11,12,
-    8,12,5,2,1,14,5,2,1,14,5,2,1,14,5,114,
-    142,0,0,0,99,0,0,0,0,0,0,0,0,0,0,0,
-    0,4,0,0,0,64,0,0,0,115,140,0,0,0,101,0,
-    90,1,100,0,90,2,100,1,90,3,101,4,100,2,100,3,
-    132,0,131,1,90,5,101,6,100,21,100,5,100,6,132,1,
-    131,1,90,7,101,6,100,22,100,7,100,8,132,1,131,1,
-    90,8,101,6,100,9,100,10,132,0,131,1,90,9,101,4,
-    100,11,100,12,132,0,131,1,90,10,101,6,100,13,100,14,
-    132,0,131,1,90,11,101,6,101,12,100,15,100,16,132,0,
-    131,1,131,1,90,13,101,6,101,12,100,17,100,18,132,0,
-    131,1,131,1,90,14,101,6,101,12,100,19,100,20,132,0,
-    131,1,131,1,90,15,100,4,83,0,41,23,218,14,70,114,
-    111,122,101,110,73,109,112,111,114,116,101,114,122,142,77,101,
-    116,97,32,112,97,116,104,32,105,109,112,111,114,116,32,102,
-    111,114,32,102,114,111,122,101,110,32,109,111,100,117,108,101,
-    115,46,10,10,32,32,32,32,65,108,108,32,109,101,116,104,
-    111,100,115,32,97,114,101,32,101,105,116,104,101,114,32,99,
-    108,97,115,115,32,111,114,32,115,116,97,116,105,99,32,109,
-    101,116,104,111,100,115,32,116,111,32,97,118,111,105,100,32,
-    116,104,101,32,110,101,101,100,32,116,111,10,32,32,32,32,
-    105,110,115,116,97,110,116,105,97,116,101,32,116,104,101,32,
-    99,108,97,115,115,46,10,10,32,32,32,32,99,1,0,0,
-    0,0,0,0,0,1,0,0,0,2,0,0,0,67,0,0,
-    0,115,12,0,0,0,100,1,106,0,124,0,106,1,131,1,
-    83,0,41,2,122,115,82,101,116,117,114,110,32,114,101,112,
-    114,32,102,111,114,32,116,104,101,32,109,111,100,117,108,101,
-    46,10,10,32,32,32,32,32,32,32,32,84,104,101,32,109,
-    101,116,104,111,100,32,105,115,32,100,101,112,114,101,99,97,
-    116,101,100,46,32,32,84,104,101,32,105,109,112,111,114,116,
-    32,109,97,99,104,105,110,101,114,121,32,100,111,101,115,32,
-    116,104,101,32,106,111,98,32,105,116,115,101,108,102,46,10,
-    10,32,32,32,32,32,32,32,32,122,22,60,109,111,100,117,
-    108,101,32,123,33,114,125,32,40,102,114,111,122,101,110,41,
-    62,41,2,114,38,0,0,0,114,1,0,0,0,41,1,218,
-    1,109,114,10,0,0,0,114,10,0,0,0,114,11,0,0,
-    0,114,86,0,0,0,250,2,0,0,115,2,0,0,0,0,
-    7,122,26,70,114,111,122,101,110,73,109,112,111,114,116,101,
-    114,46,109,111,100,117,108,101,95,114,101,112,114,78,99,4,
-    0,0,0,0,0,0,0,4,0,0,0,5,0,0,0,67,
-    0,0,0,115,32,0,0,0,116,0,106,1,124,1,131,1,
-    114,24,116,2,124,1,124,0,100,1,100,2,141,3,83,0,
-    100,0,83,0,100,0,83,0,41,3,78,90,6,102,114,111,
-    122,101,110,41,1,114,103,0,0,0,41,3,114,46,0,0,
-    0,114,75,0,0,0,114,78,0,0,0,41,4,114,143,0,
-    0,0,114,71,0,0,0,114,144,0,0,0,114,145,0,0,
-    0,114,10,0,0,0,114,10,0,0,0,114,11,0,0,0,
-    114,146,0,0,0,3,3,0,0,115,6,0,0,0,0,2,
-    10,1,14,2,122,24,70,114,111,122,101,110,73,109,112,111,
-    114,116,101,114,46,102,105,110,100,95,115,112,101,99,99,3,
-    0,0,0,0,0,0,0,3,0,0,0,2,0,0,0,67,
-    0,0,0,115,18,0,0,0,116,0,106,1,124,1,131,1,
-    114,14,124,0,83,0,100,1,83,0,41,2,122,93,70,105,
-    110,100,32,97,32,102,114,111,122,101,110,32,109,111,100,117,
-    108,101,46,10,10,32,32,32,32,32,32,32,32,84,104,105,
-    115,32,109,101,116,104,111,100,32,105,115,32,100,101,112,114,
-    101,99,97,116,101,100,46,32,32,85,115,101,32,102,105,110,
-    100,95,115,112,101,99,40,41,32,105,110,115,116,101,97,100,
-    46,10,10,32,32,32,32,32,32,32,32,78,41,2,114,46,
-    0,0,0,114,75,0,0,0,41,3,114,143,0,0,0,114,
-    71,0,0,0,114,144,0,0,0,114,10,0,0,0,114,10,
-    0,0,0,114,11,0,0,0,114,147,0,0,0,10,3,0,
-    0,115,2,0,0,0,0,7,122,26,70,114,111,122,101,110,
-    73,109,112,111,114,116,101,114,46,102,105,110,100,95,109,111,
-    100,117,108,101,99,2,0,0,0,0,0,0,0,2,0,0,
-    0,1,0,0,0,67,0,0,0,115,4,0,0,0,100,1,
-    83,0,41,2,122,42,85,115,101,32,100,101,102,97,117,108,
-    116,32,115,101,109,97,110,116,105,99,115,32,102,111,114,32,
-    109,111,100,117,108,101,32,99,114,101,97,116,105,111,110,46,
-    78,114,10,0,0,0,41,2,114,143,0,0,0,114,82,0,
-    0,0,114,10,0,0,0,114,10,0,0,0,114,11,0,0,
-    0,114,134,0,0,0,19,3,0,0,115,0,0,0,0,122,
-    28,70,114,111,122,101,110,73,109,112,111,114,116,101,114,46,
-    99,114,101,97,116,101,95,109,111,100,117,108,101,99,1,0,
-    0,0,0,0,0,0,3,0,0,0,4,0,0,0,67,0,
-    0,0,115,64,0,0,0,124,0,106,0,106,1,125,1,116,
-    2,106,3,124,1,131,1,115,36,116,4,100,1,106,5,124,
-    1,131,1,124,1,100,2,141,2,130,1,116,6,116,2,106,
-    7,124,1,131,2,125,2,116,8,124,2,124,0,106,9,131,
-    2,1,0,100,0,83,0,41,3,78,122,27,123,33,114,125,
-    32,105,115,32,110,111,116,32,97,32,102,114,111,122,101,110,
-    32,109,111,100,117,108,101,41,1,114,15,0,0,0,41,10,
-    114,89,0,0,0,114,15,0,0,0,114,46,0,0,0,114,
-    75,0,0,0,114,70,0,0,0,114,38,0,0,0,114,58,
-    0,0,0,218,17,103,101,116,95,102,114,111,122,101,110,95,
-    111,98,106,101,99,116,218,4,101,120,101,99,114,7,0,0,
-    0,41,3,114,83,0,0,0,114,15,0,0,0,218,4,99,
-    111,100,101,114,10,0,0,0,114,10,0,0,0,114,11,0,
-    0,0,114,135,0,0,0,23,3,0,0,115,12,0,0,0,
-    0,2,8,1,10,1,10,1,8,1,12,1,122,26,70,114,
-    111,122,101,110,73,109,112,111,114,116,101,114,46,101,120,101,
-    99,95,109,111,100,117,108,101,99,2,0,0,0,0,0,0,
-    0,2,0,0,0,3,0,0,0,67,0,0,0,115,10,0,
-    0,0,116,0,124,0,124,1,131,2,83,0,41,1,122,95,
-    76,111,97,100,32,97,32,102,114,111,122,101,110,32,109,111,
-    100,117,108,101,46,10,10,32,32,32,32,32,32,32,32,84,
-    104,105,115,32,109,101,116,104,111,100,32,105,115,32,100,101,
-    112,114,101,99,97,116,101,100,46,32,32,85,115,101,32,101,
-    120,101,99,95,109,111,100,117,108,101,40,41,32,105,110,115,
-    116,101,97,100,46,10,10,32,32,32,32,32,32,32,32,41,
-    1,114,84,0,0,0,41,2,114,143,0,0,0,114,71,0,
+    0,0,115,4,0,0,0,100,1,83,0,41,2,122,57,82,
+    101,116,117,114,110,32,78,111,110,101,32,97,115,32,98,117,
+    105,108,116,45,105,110,32,109,111,100,117,108,101,115,32,100,
+    111,32,110,111,116,32,104,97,118,101,32,99,111,100,101,32,
+    111,98,106,101,99,116,115,46,78,114,10,0,0,0,41,2,
+    114,143,0,0,0,114,72,0,0,0,114,10,0,0,0,114,
+    10,0,0,0,114,11,0,0,0,218,8,103,101,116,95,99,
+    111,100,101,229,2,0,0,115,2,0,0,0,0,4,122,24,
+    66,117,105,108,116,105,110,73,109,112,111,114,116,101,114,46,
+    103,101,116,95,99,111,100,101,99,2,0,0,0,0,0,0,
+    0,2,0,0,0,1,0,0,0,67,0,0,0,115,4,0,
+    0,0,100,1,83,0,41,2,122,56,82,101,116,117,114,110,
+    32,78,111,110,101,32,97,115,32,98,117,105,108,116,45,105,
+    110,32,109,111,100,117,108,101,115,32,100,111,32,110,111,116,
+    32,104,97,118,101,32,115,111,117,114,99,101,32,99,111,100,
+    101,46,78,114,10,0,0,0,41,2,114,143,0,0,0,114,
+    72,0,0,0,114,10,0,0,0,114,10,0,0,0,114,11,
+    0,0,0,218,10,103,101,116,95,115,111,117,114,99,101,235,
+    2,0,0,115,2,0,0,0,0,4,122,26,66,117,105,108,
+    116,105,110,73,109,112,111,114,116,101,114,46,103,101,116,95,
+    115,111,117,114,99,101,99,2,0,0,0,0,0,0,0,2,
+    0,0,0,1,0,0,0,67,0,0,0,115,4,0,0,0,
+    100,1,83,0,41,2,122,52,82,101,116,117,114,110,32,70,
+    97,108,115,101,32,97,115,32,98,117,105,108,116,45,105,110,
+    32,109,111,100,117,108,101,115,32,97,114,101,32,110,101,118,
+    101,114,32,112,97,99,107,97,103,101,115,46,70,114,10,0,
+    0,0,41,2,114,143,0,0,0,114,72,0,0,0,114,10,
+    0,0,0,114,10,0,0,0,114,11,0,0,0,114,106,0,
+    0,0,241,2,0,0,115,2,0,0,0,0,4,122,26,66,
+    117,105,108,116,105,110,73,109,112,111,114,116,101,114,46,105,
+    115,95,112,97,99,107,97,103,101,41,2,78,78,41,1,78,
+    41,17,114,1,0,0,0,114,0,0,0,0,114,2,0,0,
+    0,114,3,0,0,0,218,12,115,116,97,116,105,99,109,101,
+    116,104,111,100,114,87,0,0,0,218,11,99,108,97,115,115,
+    109,101,116,104,111,100,114,146,0,0,0,114,147,0,0,0,
+    114,135,0,0,0,114,136,0,0,0,114,75,0,0,0,114,
+    148,0,0,0,114,149,0,0,0,114,106,0,0,0,114,85,
+    0,0,0,114,138,0,0,0,114,10,0,0,0,114,10,0,
+    0,0,114,10,0,0,0,114,11,0,0,0,114,142,0,0,
+    0,177,2,0,0,115,30,0,0,0,8,7,4,2,12,9,
+    2,1,12,8,2,1,12,11,12,8,12,5,2,1,14,5,
+    2,1,14,5,2,1,14,5,114,142,0,0,0,99,0,0,
+    0,0,0,0,0,0,0,0,0,0,4,0,0,0,64,0,
+    0,0,115,140,0,0,0,101,0,90,1,100,0,90,2,100,
+    1,90,3,101,4,100,2,100,3,132,0,131,1,90,5,101,
+    6,100,21,100,5,100,6,132,1,131,1,90,7,101,6,100,
+    22,100,7,100,8,132,1,131,1,90,8,101,6,100,9,100,
+    10,132,0,131,1,90,9,101,4,100,11,100,12,132,0,131,
+    1,90,10,101,6,100,13,100,14,132,0,131,1,90,11,101,
+    6,101,12,100,15,100,16,132,0,131,1,131,1,90,13,101,
+    6,101,12,100,17,100,18,132,0,131,1,131,1,90,14,101,
+    6,101,12,100,19,100,20,132,0,131,1,131,1,90,15,100,
+    4,83,0,41,23,218,14,70,114,111,122,101,110,73,109,112,
+    111,114,116,101,114,122,142,77,101,116,97,32,112,97,116,104,
+    32,105,109,112,111,114,116,32,102,111,114,32,102,114,111,122,
+    101,110,32,109,111,100,117,108,101,115,46,10,10,32,32,32,
+    32,65,108,108,32,109,101,116,104,111,100,115,32,97,114,101,
+    32,101,105,116,104,101,114,32,99,108,97,115,115,32,111,114,
+    32,115,116,97,116,105,99,32,109,101,116,104,111,100,115,32,
+    116,111,32,97,118,111,105,100,32,116,104,101,32,110,101,101,
+    100,32,116,111,10,32,32,32,32,105,110,115,116,97,110,116,
+    105,97,116,101,32,116,104,101,32,99,108,97,115,115,46,10,
+    10,32,32,32,32,99,1,0,0,0,0,0,0,0,1,0,
+    0,0,2,0,0,0,67,0,0,0,115,12,0,0,0,100,
+    1,106,0,124,0,106,1,131,1,83,0,41,2,122,115,82,
+    101,116,117,114,110,32,114,101,112,114,32,102,111,114,32,116,
+    104,101,32,109,111,100,117,108,101,46,10,10,32,32,32,32,
+    32,32,32,32,84,104,101,32,109,101,116,104,111,100,32,105,
+    115,32,100,101,112,114,101,99,97,116,101,100,46,32,32,84,
+    104,101,32,105,109,112,111,114,116,32,109,97,99,104,105,110,
+    101,114,121,32,100,111,101,115,32,116,104,101,32,106,111,98,
+    32,105,116,115,101,108,102,46,10,10,32,32,32,32,32,32,
+    32,32,122,22,60,109,111,100,117,108,101,32,123,33,114,125,
+    32,40,102,114,111,122,101,110,41,62,41,2,114,38,0,0,
+    0,114,1,0,0,0,41,1,218,1,109,114,10,0,0,0,
+    114,10,0,0,0,114,11,0,0,0,114,87,0,0,0,3,
+    3,0,0,115,2,0,0,0,0,7,122,26,70,114,111,122,
+    101,110,73,109,112,111,114,116,101,114,46,109,111,100,117,108,
+    101,95,114,101,112,114,78,99,4,0,0,0,0,0,0,0,
+    4,0,0,0,5,0,0,0,67,0,0,0,115,32,0,0,
+    0,116,0,106,1,124,1,131,1,114,24,116,2,124,1,124,
+    0,100,1,100,2,141,3,83,0,100,0,83,0,100,0,83,
+    0,41,3,78,90,6,102,114,111,122,101,110,41,1,114,104,
+    0,0,0,41,3,114,49,0,0,0,114,76,0,0,0,114,
+    79,0,0,0,41,4,114,143,0,0,0,114,72,0,0,0,
+    114,144,0,0,0,114,145,0,0,0,114,10,0,0,0,114,
+    10,0,0,0,114,11,0,0,0,114,146,0,0,0,12,3,
+    0,0,115,6,0,0,0,0,2,10,1,14,2,122,24,70,
+    114,111,122,101,110,73,109,112,111,114,116,101,114,46,102,105,
+    110,100,95,115,112,101,99,99,3,0,0,0,0,0,0,0,
+    3,0,0,0,2,0,0,0,67,0,0,0,115,18,0,0,
+    0,116,0,106,1,124,1,131,1,114,14,124,0,83,0,100,
+    1,83,0,41,2,122,93,70,105,110,100,32,97,32,102,114,
+    111,122,101,110,32,109,111,100,117,108,101,46,10,10,32,32,
+    32,32,32,32,32,32,84,104,105,115,32,109,101,116,104,111,
+    100,32,105,115,32,100,101,112,114,101,99,97,116,101,100,46,
+    32,32,85,115,101,32,102,105,110,100,95,115,112,101,99,40,
+    41,32,105,110,115,116,101,97,100,46,10,10,32,32,32,32,
+    32,32,32,32,78,41,2,114,49,0,0,0,114,76,0,0,
+    0,41,3,114,143,0,0,0,114,72,0,0,0,114,144,0,
     0,0,114,10,0,0,0,114,10,0,0,0,114,11,0,0,
-    0,114,138,0,0,0,32,3,0,0,115,2,0,0,0,0,
+    0,114,147,0,0,0,19,3,0,0,115,2,0,0,0,0,
     7,122,26,70,114,111,122,101,110,73,109,112,111,114,116,101,
-    114,46,108,111,97,100,95,109,111,100,117,108,101,99,2,0,
-    0,0,0,0,0,0,2,0,0,0,2,0,0,0,67,0,
-    0,0,115,10,0,0,0,116,0,106,1,124,1,131,1,83,
-    0,41,1,122,45,82,101,116,117,114,110,32,116,104,101,32,
-    99,111,100,101,32,111,98,106,101,99,116,32,102,111,114,32,
-    116,104,101,32,102,114,111,122,101,110,32,109,111,100,117,108,
-    101,46,41,2,114,46,0,0,0,114,154,0,0,0,41,2,
-    114,143,0,0,0,114,71,0,0,0,114,10,0,0,0,114,
-    10,0,0,0,114,11,0,0,0,114,148,0,0,0,41,3,
-    0,0,115,2,0,0,0,0,4,122,23,70,114,111,122,101,
-    110,73,109,112,111,114,116,101,114,46,103,101,116,95,99,111,
-    100,101,99,2,0,0,0,0,0,0,0,2,0,0,0,1,
-    0,0,0,67,0,0,0,115,4,0,0,0,100,1,83,0,
-    41,2,122,54,82,101,116,117,114,110,32,78,111,110,101,32,
-    97,115,32,102,114,111,122,101,110,32,109,111,100,117,108,101,
-    115,32,100,111,32,110,111,116,32,104,97,118,101,32,115,111,
-    117,114,99,101,32,99,111,100,101,46,78,114,10,0,0,0,
-    41,2,114,143,0,0,0,114,71,0,0,0,114,10,0,0,
-    0,114,10,0,0,0,114,11,0,0,0,114,149,0,0,0,
-    47,3,0,0,115,2,0,0,0,0,4,122,25,70,114,111,
-    122,101,110,73,109,112,111,114,116,101,114,46,103,101,116,95,
-    115,111,117,114,99,101,99,2,0,0,0,0,0,0,0,2,
+    114,46,102,105,110,100,95,109,111,100,117,108,101,99,2,0,
+    0,0,0,0,0,0,2,0,0,0,1,0,0,0,67,0,
+    0,0,115,4,0,0,0,100,1,83,0,41,2,122,42,85,
+    115,101,32,100,101,102,97,117,108,116,32,115,101,109,97,110,
+    116,105,99,115,32,102,111,114,32,109,111,100,117,108,101,32,
+    99,114,101,97,116,105,111,110,46,78,114,10,0,0,0,41,
+    2,114,143,0,0,0,114,83,0,0,0,114,10,0,0,0,
+    114,10,0,0,0,114,11,0,0,0,114,135,0,0,0,28,
+    3,0,0,115,0,0,0,0,122,28,70,114,111,122,101,110,
+    73,109,112,111,114,116,101,114,46,99,114,101,97,116,101,95,
+    109,111,100,117,108,101,99,1,0,0,0,0,0,0,0,3,
+    0,0,0,4,0,0,0,67,0,0,0,115,64,0,0,0,
+    124,0,106,0,106,1,125,1,116,2,106,3,124,1,131,1,
+    115,36,116,4,100,1,106,5,124,1,131,1,124,1,100,2,
+    141,2,130,1,116,6,116,2,106,7,124,1,131,2,125,2,
+    116,8,124,2,124,0,106,9,131,2,1,0,100,0,83,0,
+    41,3,78,122,27,123,33,114,125,32,105,115,32,110,111,116,
+    32,97,32,102,114,111,122,101,110,32,109,111,100,117,108,101,
+    41,1,114,15,0,0,0,41,10,114,90,0,0,0,114,15,
+    0,0,0,114,49,0,0,0,114,76,0,0,0,114,71,0,
+    0,0,114,38,0,0,0,114,59,0,0,0,218,17,103,101,
+    116,95,102,114,111,122,101,110,95,111,98,106,101,99,116,218,
+    4,101,120,101,99,114,7,0,0,0,41,3,114,84,0,0,
+    0,114,15,0,0,0,218,4,99,111,100,101,114,10,0,0,
+    0,114,10,0,0,0,114,11,0,0,0,114,136,0,0,0,
+    32,3,0,0,115,12,0,0,0,0,2,8,1,10,1,10,
+    1,8,1,12,1,122,26,70,114,111,122,101,110,73,109,112,
+    111,114,116,101,114,46,101,120,101,99,95,109,111,100,117,108,
+    101,99,2,0,0,0,0,0,0,0,2,0,0,0,3,0,
+    0,0,67,0,0,0,115,10,0,0,0,116,0,124,0,124,
+    1,131,2,83,0,41,1,122,95,76,111,97,100,32,97,32,
+    102,114,111,122,101,110,32,109,111,100,117,108,101,46,10,10,
+    32,32,32,32,32,32,32,32,84,104,105,115,32,109,101,116,
+    104,111,100,32,105,115,32,100,101,112,114,101,99,97,116,101,
+    100,46,32,32,85,115,101,32,101,120,101,99,95,109,111,100,
+    117,108,101,40,41,32,105,110,115,116,101,97,100,46,10,10,
+    32,32,32,32,32,32,32,32,41,1,114,85,0,0,0,41,
+    2,114,143,0,0,0,114,72,0,0,0,114,10,0,0,0,
+    114,10,0,0,0,114,11,0,0,0,114,138,0,0,0,41,
+    3,0,0,115,2,0,0,0,0,7,122,26,70,114,111,122,
+    101,110,73,109,112,111,114,116,101,114,46,108,111,97,100,95,
+    109,111,100,117,108,101,99,2,0,0,0,0,0,0,0,2,
     0,0,0,2,0,0,0,67,0,0,0,115,10,0,0,0,
-    116,0,106,1,124,1,131,1,83,0,41,1,122,46,82,101,
-    116,117,114,110,32,84,114,117,101,32,105,102,32,116,104,101,
-    32,102,114,111,122,101,110,32,109,111,100,117,108,101,32,105,
-    115,32,97,32,112,97,99,107,97,103,101,46,41,2,114,46,
-    0,0,0,90,17,105,115,95,102,114,111,122,101,110,95,112,
-    97,99,107,97,103,101,41,2,114,143,0,0,0,114,71,0,
-    0,0,114,10,0,0,0,114,10,0,0,0,114,11,0,0,
-    0,114,105,0,0,0,53,3,0,0,115,2,0,0,0,0,
-    4,122,25,70,114,111,122,101,110,73,109,112,111,114,116,101,
-    114,46,105,115,95,112,97,99,107,97,103,101,41,2,78,78,
-    41,1,78,41,16,114,1,0,0,0,114,0,0,0,0,114,
-    2,0,0,0,114,3,0,0,0,114,150,0,0,0,114,86,
-    0,0,0,114,151,0,0,0,114,146,0,0,0,114,147,0,
-    0,0,114,134,0,0,0,114,135,0,0,0,114,138,0,0,
-    0,114,77,0,0,0,114,148,0,0,0,114,149,0,0,0,
-    114,105,0,0,0,114,10,0,0,0,114,10,0,0,0,114,
-    10,0,0,0,114,11,0,0,0,114,152,0,0,0,241,2,
-    0,0,115,30,0,0,0,8,7,4,2,12,9,2,1,12,
-    6,2,1,12,8,12,4,12,9,12,9,2,1,14,5,2,
-    1,14,5,2,1,114,152,0,0,0,99,0,0,0,0,0,
-    0,0,0,0,0,0,0,2,0,0,0,64,0,0,0,115,
-    32,0,0,0,101,0,90,1,100,0,90,2,100,1,90,3,
-    100,2,100,3,132,0,90,4,100,4,100,5,132,0,90,5,
-    100,6,83,0,41,7,218,18,95,73,109,112,111,114,116,76,
-    111,99,107,67,111,110,116,101,120,116,122,36,67,111,110,116,
-    101,120,116,32,109,97,110,97,103,101,114,32,102,111,114,32,
-    116,104,101,32,105,109,112,111,114,116,32,108,111,99,107,46,
-    99,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,
-    0,67,0,0,0,115,12,0,0,0,116,0,106,1,131,0,
-    1,0,100,1,83,0,41,2,122,24,65,99,113,117,105,114,
-    101,32,116,104,101,32,105,109,112,111,114,116,32,108,111,99,
-    107,46,78,41,2,114,46,0,0,0,114,137,0,0,0,41,
-    1,114,26,0,0,0,114,10,0,0,0,114,10,0,0,0,
-    114,11,0,0,0,114,48,0,0,0,66,3,0,0,115,2,
-    0,0,0,0,2,122,28,95,73,109,112,111,114,116,76,111,
-    99,107,67,111,110,116,101,120,116,46,95,95,101,110,116,101,
-    114,95,95,99,4,0,0,0,0,0,0,0,4,0,0,0,
-    1,0,0,0,67,0,0,0,115,12,0,0,0,116,0,106,
-    1,131,0,1,0,100,1,83,0,41,2,122,60,82,101,108,
-    101,97,115,101,32,116,104,101,32,105,109,112,111,114,116,32,
-    108,111,99,107,32,114,101,103,97,114,100,108,101,115,115,32,
-    111,102,32,97,110,121,32,114,97,105,115,101,100,32,101,120,
-    99,101,112,116,105,111,110,115,46,78,41,2,114,46,0,0,
-    0,114,47,0,0,0,41,4,114,26,0,0,0,90,8,101,
-    120,99,95,116,121,112,101,90,9,101,120,99,95,118,97,108,
-    117,101,90,13,101,120,99,95,116,114,97,99,101,98,97,99,
-    107,114,10,0,0,0,114,10,0,0,0,114,11,0,0,0,
-    114,50,0,0,0,70,3,0,0,115,2,0,0,0,0,2,
-    122,27,95,73,109,112,111,114,116,76,111,99,107,67,111,110,
-    116,101,120,116,46,95,95,101,120,105,116,95,95,78,41,6,
-    114,1,0,0,0,114,0,0,0,0,114,2,0,0,0,114,
-    3,0,0,0,114,48,0,0,0,114,50,0,0,0,114,10,
+    116,0,106,1,124,1,131,1,83,0,41,1,122,45,82,101,
+    116,117,114,110,32,116,104,101,32,99,111,100,101,32,111,98,
+    106,101,99,116,32,102,111,114,32,116,104,101,32,102,114,111,
+    122,101,110,32,109,111,100,117,108,101,46,41,2,114,49,0,
+    0,0,114,154,0,0,0,41,2,114,143,0,0,0,114,72,
     0,0,0,114,10,0,0,0,114,10,0,0,0,114,11,0,
-    0,0,114,157,0,0,0,62,3,0,0,115,6,0,0,0,
-    8,2,4,2,8,4,114,157,0,0,0,99,3,0,0,0,
-    0,0,0,0,5,0,0,0,4,0,0,0,67,0,0,0,
-    115,64,0,0,0,124,1,106,0,100,1,124,2,100,2,24,
-    0,131,2,125,3,116,1,124,3,131,1,124,2,107,0,114,
-    36,116,2,100,3,131,1,130,1,124,3,100,4,25,0,125,
-    4,124,0,114,60,100,5,106,3,124,4,124,0,131,2,83,
-    0,124,4,83,0,41,6,122,50,82,101,115,111,108,118,101,
-    32,97,32,114,101,108,97,116,105,118,101,32,109,111,100,117,
-    108,101,32,110,97,109,101,32,116,111,32,97,110,32,97,98,
-    115,111,108,117,116,101,32,111,110,101,46,114,117,0,0,0,
-    114,33,0,0,0,122,50,97,116,116,101,109,112,116,101,100,
-    32,114,101,108,97,116,105,118,101,32,105,109,112,111,114,116,
-    32,98,101,121,111,110,100,32,116,111,112,45,108,101,118,101,
-    108,32,112,97,99,107,97,103,101,114,19,0,0,0,122,5,
-    123,125,46,123,125,41,4,218,6,114,115,112,108,105,116,218,
-    3,108,101,110,218,10,86,97,108,117,101,69,114,114,111,114,
-    114,38,0,0,0,41,5,114,15,0,0,0,218,7,112,97,
-    99,107,97,103,101,218,5,108,101,118,101,108,90,4,98,105,
-    116,115,90,4,98,97,115,101,114,10,0,0,0,114,10,0,
-    0,0,114,11,0,0,0,218,13,95,114,101,115,111,108,118,
-    101,95,110,97,109,101,75,3,0,0,115,10,0,0,0,0,
-    2,16,1,12,1,8,1,8,1,114,163,0,0,0,99,3,
-    0,0,0,0,0,0,0,4,0,0,0,3,0,0,0,67,
-    0,0,0,115,34,0,0,0,124,0,106,0,124,1,124,2,
-    131,2,125,3,124,3,100,0,107,8,114,24,100,0,83,0,
-    116,1,124,1,124,3,131,2,83,0,41,1,78,41,2,114,
-    147,0,0,0,114,78,0,0,0,41,4,218,6,102,105,110,
-    100,101,114,114,15,0,0,0,114,144,0,0,0,114,93,0,
-    0,0,114,10,0,0,0,114,10,0,0,0,114,11,0,0,
-    0,218,17,95,102,105,110,100,95,115,112,101,99,95,108,101,
-    103,97,99,121,84,3,0,0,115,8,0,0,0,0,3,12,
-    1,8,1,4,1,114,165,0,0,0,99,3,0,0,0,0,
-    0,0,0,10,0,0,0,27,0,0,0,67,0,0,0,115,
-    242,0,0,0,116,0,106,1,125,3,124,3,100,1,107,8,
-    114,22,116,2,100,2,131,1,130,1,124,3,115,38,116,3,
-    106,4,100,3,116,5,131,2,1,0,124,0,116,0,106,6,
-    107,6,125,4,120,188,124,3,68,0,93,176,125,5,116,7,
-    131,0,143,72,1,0,121,10,124,5,106,8,125,6,87,0,
-    110,42,4,0,116,9,107,10,114,118,1,0,1,0,1,0,
-    116,10,124,5,124,0,124,1,131,3,125,7,124,7,100,1,
-    107,8,114,114,119,54,89,0,110,14,88,0,124,6,124,0,
-    124,1,124,2,131,3,125,7,87,0,100,1,81,0,82,0,
-    88,0,124,7,100,1,107,9,114,54,124,4,12,0,114,226,
-    124,0,116,0,106,6,107,6,114,226,116,0,106,6,124,0,
-    25,0,125,8,121,10,124,8,106,11,125,9,87,0,110,20,
-    4,0,116,9,107,10,114,206,1,0,1,0,1,0,124,7,
-    83,0,88,0,124,9,100,1,107,8,114,220,124,7,83,0,
-    124,9,83,0,113,54,124,7,83,0,113,54,87,0,100,1,
-    83,0,100,1,83,0,41,4,122,21,70,105,110,100,32,97,
-    32,109,111,100,117,108,101,39,115,32,115,112,101,99,46,78,
-    122,53,115,121,115,46,109,101,116,97,95,112,97,116,104,32,
-    105,115,32,78,111,110,101,44,32,80,121,116,104,111,110,32,
-    105,115,32,108,105,107,101,108,121,32,115,104,117,116,116,105,
-    110,103,32,100,111,119,110,122,22,115,121,115,46,109,101,116,
-    97,95,112,97,116,104,32,105,115,32,101,109,112,116,121,41,
-    12,114,14,0,0,0,218,9,109,101,116,97,95,112,97,116,
-    104,114,70,0,0,0,218,9,95,119,97,114,110,105,110,103,
-    115,218,4,119,97,114,110,218,13,73,109,112,111,114,116,87,
-    97,114,110,105,110,103,114,79,0,0,0,114,157,0,0,0,
-    114,146,0,0,0,114,90,0,0,0,114,165,0,0,0,114,
-    89,0,0,0,41,10,114,15,0,0,0,114,144,0,0,0,
-    114,145,0,0,0,114,166,0,0,0,90,9,105,115,95,114,
-    101,108,111,97,100,114,164,0,0,0,114,146,0,0,0,114,
-    82,0,0,0,114,83,0,0,0,114,89,0,0,0,114,10,
-    0,0,0,114,10,0,0,0,114,11,0,0,0,218,10,95,
-    102,105,110,100,95,115,112,101,99,93,3,0,0,115,54,0,
-    0,0,0,2,6,1,8,2,8,3,4,1,12,5,10,1,
-    10,1,8,1,2,1,10,1,14,1,12,1,8,1,8,2,
-    22,1,8,2,16,1,10,1,2,1,10,1,14,4,6,2,
-    8,1,4,2,6,2,8,2,114,170,0,0,0,99,3,0,
-    0,0,0,0,0,0,4,0,0,0,4,0,0,0,67,0,
-    0,0,115,140,0,0,0,116,0,124,0,116,1,131,2,115,
-    28,116,2,100,1,106,3,116,4,124,0,131,1,131,1,131,
-    1,130,1,124,2,100,2,107,0,114,44,116,5,100,3,131,
-    1,130,1,124,2,100,2,107,4,114,114,116,0,124,1,116,
-    1,131,2,115,72,116,2,100,4,131,1,130,1,110,42,124,
-    1,115,86,116,6,100,5,131,1,130,1,110,28,124,1,116,
-    7,106,8,107,7,114,114,100,6,125,3,116,9,124,3,106,
-    3,124,1,131,1,131,1,130,1,124,0,12,0,114,136,124,
-    2,100,2,107,2,114,136,116,5,100,7,131,1,130,1,100,
-    8,83,0,41,9,122,28,86,101,114,105,102,121,32,97,114,
-    103,117,109,101,110,116,115,32,97,114,101,32,34,115,97,110,
-    101,34,46,122,31,109,111,100,117,108,101,32,110,97,109,101,
-    32,109,117,115,116,32,98,101,32,115,116,114,44,32,110,111,
-    116,32,123,125,114,19,0,0,0,122,18,108,101,118,101,108,
-    32,109,117,115,116,32,98,101,32,62,61,32,48,122,31,95,
-    95,112,97,99,107,97,103,101,95,95,32,110,111,116,32,115,
-    101,116,32,116,111,32,97,32,115,116,114,105,110,103,122,54,
+    0,0,114,148,0,0,0,50,3,0,0,115,2,0,0,0,
+    0,4,122,23,70,114,111,122,101,110,73,109,112,111,114,116,
+    101,114,46,103,101,116,95,99,111,100,101,99,2,0,0,0,
+    0,0,0,0,2,0,0,0,1,0,0,0,67,0,0,0,
+    115,4,0,0,0,100,1,83,0,41,2,122,54,82,101,116,
+    117,114,110,32,78,111,110,101,32,97,115,32,102,114,111,122,
+    101,110,32,109,111,100,117,108,101,115,32,100,111,32,110,111,
+    116,32,104,97,118,101,32,115,111,117,114,99,101,32,99,111,
+    100,101,46,78,114,10,0,0,0,41,2,114,143,0,0,0,
+    114,72,0,0,0,114,10,0,0,0,114,10,0,0,0,114,
+    11,0,0,0,114,149,0,0,0,56,3,0,0,115,2,0,
+    0,0,0,4,122,25,70,114,111,122,101,110,73,109,112,111,
+    114,116,101,114,46,103,101,116,95,115,111,117,114,99,101,99,
+    2,0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,
+    67,0,0,0,115,10,0,0,0,116,0,106,1,124,1,131,
+    1,83,0,41,1,122,46,82,101,116,117,114,110,32,84,114,
+    117,101,32,105,102,32,116,104,101,32,102,114,111,122,101,110,
+    32,109,111,100,117,108,101,32,105,115,32,97,32,112,97,99,
+    107,97,103,101,46,41,2,114,49,0,0,0,90,17,105,115,
+    95,102,114,111,122,101,110,95,112,97,99,107,97,103,101,41,
+    2,114,143,0,0,0,114,72,0,0,0,114,10,0,0,0,
+    114,10,0,0,0,114,11,0,0,0,114,106,0,0,0,62,
+    3,0,0,115,2,0,0,0,0,4,122,25,70,114,111,122,
+    101,110,73,109,112,111,114,116,101,114,46,105,115,95,112,97,
+    99,107,97,103,101,41,2,78,78,41,1,78,41,16,114,1,
+    0,0,0,114,0,0,0,0,114,2,0,0,0,114,3,0,
+    0,0,114,150,0,0,0,114,87,0,0,0,114,151,0,0,
+    0,114,146,0,0,0,114,147,0,0,0,114,135,0,0,0,
+    114,136,0,0,0,114,138,0,0,0,114,78,0,0,0,114,
+    148,0,0,0,114,149,0,0,0,114,106,0,0,0,114,10,
+    0,0,0,114,10,0,0,0,114,10,0,0,0,114,11,0,
+    0,0,114,152,0,0,0,250,2,0,0,115,30,0,0,0,
+    8,7,4,2,12,9,2,1,12,6,2,1,12,8,12,4,
+    12,9,12,9,2,1,14,5,2,1,14,5,2,1,114,152,
+    0,0,0,99,0,0,0,0,0,0,0,0,0,0,0,0,
+    2,0,0,0,64,0,0,0,115,32,0,0,0,101,0,90,
+    1,100,0,90,2,100,1,90,3,100,2,100,3,132,0,90,
+    4,100,4,100,5,132,0,90,5,100,6,83,0,41,7,218,
+    18,95,73,109,112,111,114,116,76,111,99,107,67,111,110,116,
+    101,120,116,122,36,67,111,110,116,101,120,116,32,109,97,110,
+    97,103,101,114,32,102,111,114,32,116,104,101,32,105,109,112,
+    111,114,116,32,108,111,99,107,46,99,1,0,0,0,0,0,
+    0,0,1,0,0,0,1,0,0,0,67,0,0,0,115,12,
+    0,0,0,116,0,106,1,131,0,1,0,100,1,83,0,41,
+    2,122,24,65,99,113,117,105,114,101,32,116,104,101,32,105,
+    109,112,111,114,116,32,108,111,99,107,46,78,41,2,114,49,
+    0,0,0,114,50,0,0,0,41,1,114,26,0,0,0,114,
+    10,0,0,0,114,10,0,0,0,114,11,0,0,0,114,46,
+    0,0,0,75,3,0,0,115,2,0,0,0,0,2,122,28,
+    95,73,109,112,111,114,116,76,111,99,107,67,111,110,116,101,
+    120,116,46,95,95,101,110,116,101,114,95,95,99,4,0,0,
+    0,0,0,0,0,4,0,0,0,1,0,0,0,67,0,0,
+    0,115,12,0,0,0,116,0,106,1,131,0,1,0,100,1,
+    83,0,41,2,122,60,82,101,108,101,97,115,101,32,116,104,
+    101,32,105,109,112,111,114,116,32,108,111,99,107,32,114,101,
+    103,97,114,100,108,101,115,115,32,111,102,32,97,110,121,32,
+    114,97,105,115,101,100,32,101,120,99,101,112,116,105,111,110,
+    115,46,78,41,2,114,49,0,0,0,114,52,0,0,0,41,
+    4,114,26,0,0,0,90,8,101,120,99,95,116,121,112,101,
+    90,9,101,120,99,95,118,97,108,117,101,90,13,101,120,99,
+    95,116,114,97,99,101,98,97,99,107,114,10,0,0,0,114,
+    10,0,0,0,114,11,0,0,0,114,48,0,0,0,79,3,
+    0,0,115,2,0,0,0,0,2,122,27,95,73,109,112,111,
+    114,116,76,111,99,107,67,111,110,116,101,120,116,46,95,95,
+    101,120,105,116,95,95,78,41,6,114,1,0,0,0,114,0,
+    0,0,0,114,2,0,0,0,114,3,0,0,0,114,46,0,
+    0,0,114,48,0,0,0,114,10,0,0,0,114,10,0,0,
+    0,114,10,0,0,0,114,11,0,0,0,114,157,0,0,0,
+    71,3,0,0,115,6,0,0,0,8,2,4,2,8,4,114,
+    157,0,0,0,99,3,0,0,0,0,0,0,0,5,0,0,
+    0,4,0,0,0,67,0,0,0,115,64,0,0,0,124,1,
+    106,0,100,1,124,2,100,2,24,0,131,2,125,3,116,1,
+    124,3,131,1,124,2,107,0,114,36,116,2,100,3,131,1,
+    130,1,124,3,100,4,25,0,125,4,124,0,114,60,100,5,
+    106,3,124,4,124,0,131,2,83,0,124,4,83,0,41,6,
+    122,50,82,101,115,111,108,118,101,32,97,32,114,101,108,97,
+    116,105,118,101,32,109,111,100,117,108,101,32,110,97,109,101,
+    32,116,111,32,97,110,32,97,98,115,111,108,117,116,101,32,
+    111,110,101,46,114,118,0,0,0,114,33,0,0,0,122,50,
     97,116,116,101,109,112,116,101,100,32,114,101,108,97,116,105,
-    118,101,32,105,109,112,111,114,116,32,119,105,116,104,32,110,
-    111,32,107,110,111,119,110,32,112,97,114,101,110,116,32,112,
-    97,99,107,97,103,101,122,61,80,97,114,101,110,116,32,109,
-    111,100,117,108,101,32,123,33,114,125,32,110,111,116,32,108,
-    111,97,100,101,100,44,32,99,97,110,110,111,116,32,112,101,
-    114,102,111,114,109,32,114,101,108,97,116,105,118,101,32,105,
-    109,112,111,114,116,122,17,69,109,112,116,121,32,109,111,100,
-    117,108,101,32,110,97,109,101,78,41,10,218,10,105,115,105,
-    110,115,116,97,110,99,101,218,3,115,116,114,218,9,84,121,
-    112,101,69,114,114,111,114,114,38,0,0,0,114,13,0,0,
-    0,114,160,0,0,0,114,70,0,0,0,114,14,0,0,0,
-    114,79,0,0,0,218,11,83,121,115,116,101,109,69,114,114,
-    111,114,41,4,114,15,0,0,0,114,161,0,0,0,114,162,
-    0,0,0,114,139,0,0,0,114,10,0,0,0,114,10,0,
+    118,101,32,105,109,112,111,114,116,32,98,101,121,111,110,100,
+    32,116,111,112,45,108,101,118,101,108,32,112,97,99,107,97,
+    103,101,114,19,0,0,0,122,5,123,125,46,123,125,41,4,
+    218,6,114,115,112,108,105,116,218,3,108,101,110,218,10,86,
+    97,108,117,101,69,114,114,111,114,114,38,0,0,0,41,5,
+    114,15,0,0,0,218,7,112,97,99,107,97,103,101,218,5,
+    108,101,118,101,108,90,4,98,105,116,115,90,4,98,97,115,
+    101,114,10,0,0,0,114,10,0,0,0,114,11,0,0,0,
+    218,13,95,114,101,115,111,108,118,101,95,110,97,109,101,84,
+    3,0,0,115,10,0,0,0,0,2,16,1,12,1,8,1,
+    8,1,114,163,0,0,0,99,3,0,0,0,0,0,0,0,
+    4,0,0,0,3,0,0,0,67,0,0,0,115,34,0,0,
+    0,124,0,106,0,124,1,124,2,131,2,125,3,124,3,100,
+    0,107,8,114,24,100,0,83,0,116,1,124,1,124,3,131,
+    2,83,0,41,1,78,41,2,114,147,0,0,0,114,79,0,
+    0,0,41,4,218,6,102,105,110,100,101,114,114,15,0,0,
+    0,114,144,0,0,0,114,94,0,0,0,114,10,0,0,0,
+    114,10,0,0,0,114,11,0,0,0,218,17,95,102,105,110,
+    100,95,115,112,101,99,95,108,101,103,97,99,121,93,3,0,
+    0,115,8,0,0,0,0,3,12,1,8,1,4,1,114,165,
+    0,0,0,99,3,0,0,0,0,0,0,0,10,0,0,0,
+    27,0,0,0,67,0,0,0,115,242,0,0,0,116,0,106,
+    1,125,3,124,3,100,1,107,8,114,22,116,2,100,2,131,
+    1,130,1,124,3,115,38,116,3,106,4,100,3,116,5,131,
+    2,1,0,124,0,116,0,106,6,107,6,125,4,120,188,124,
+    3,68,0,93,176,125,5,116,7,131,0,143,72,1,0,121,
+    10,124,5,106,8,125,6,87,0,110,42,4,0,116,9,107,
+    10,114,118,1,0,1,0,1,0,116,10,124,5,124,0,124,
+    1,131,3,125,7,124,7,100,1,107,8,114,114,119,54,89,
+    0,110,14,88,0,124,6,124,0,124,1,124,2,131,3,125,
+    7,87,0,100,1,81,0,82,0,88,0,124,7,100,1,107,
+    9,114,54,124,4,12,0,114,226,124,0,116,0,106,6,107,
+    6,114,226,116,0,106,6,124,0,25,0,125,8,121,10,124,
+    8,106,11,125,9,87,0,110,20,4,0,116,9,107,10,114,
+    206,1,0,1,0,1,0,124,7,83,0,88,0,124,9,100,
+    1,107,8,114,220,124,7,83,0,124,9,83,0,113,54,124,
+    7,83,0,113,54,87,0,100,1,83,0,100,1,83,0,41,
+    4,122,21,70,105,110,100,32,97,32,109,111,100,117,108,101,
+    39,115,32,115,112,101,99,46,78,122,53,115,121,115,46,109,
+    101,116,97,95,112,97,116,104,32,105,115,32,78,111,110,101,
+    44,32,80,121,116,104,111,110,32,105,115,32,108,105,107,101,
+    108,121,32,115,104,117,116,116,105,110,103,32,100,111,119,110,
+    122,22,115,121,115,46,109,101,116,97,95,112,97,116,104,32,
+    105,115,32,101,109,112,116,121,41,12,114,14,0,0,0,218,
+    9,109,101,116,97,95,112,97,116,104,114,71,0,0,0,218,
+    9,95,119,97,114,110,105,110,103,115,218,4,119,97,114,110,
+    218,13,73,109,112,111,114,116,87,97,114,110,105,110,103,114,
+    80,0,0,0,114,157,0,0,0,114,146,0,0,0,114,91,
+    0,0,0,114,165,0,0,0,114,90,0,0,0,41,10,114,
+    15,0,0,0,114,144,0,0,0,114,145,0,0,0,114,166,
+    0,0,0,90,9,105,115,95,114,101,108,111,97,100,114,164,
+    0,0,0,114,146,0,0,0,114,83,0,0,0,114,84,0,
+    0,0,114,90,0,0,0,114,10,0,0,0,114,10,0,0,
+    0,114,11,0,0,0,218,10,95,102,105,110,100,95,115,112,
+    101,99,102,3,0,0,115,54,0,0,0,0,2,6,1,8,
+    2,8,3,4,1,12,5,10,1,10,1,8,1,2,1,10,
+    1,14,1,12,1,8,1,8,2,22,1,8,2,16,1,10,
+    1,2,1,10,1,14,4,6,2,8,1,4,2,6,2,8,
+    2,114,170,0,0,0,99,3,0,0,0,0,0,0,0,3,
+    0,0,0,4,0,0,0,67,0,0,0,115,110,0,0,0,
+    116,0,124,0,116,1,131,2,115,28,116,2,100,1,106,3,
+    116,4,124,0,131,1,131,1,131,1,130,1,124,2,100,2,
+    107,0,114,44,116,5,100,3,131,1,130,1,124,2,100,2,
+    107,4,114,84,116,0,124,1,116,1,131,2,115,72,116,2,
+    100,4,131,1,130,1,110,12,124,1,115,84,116,6,100,5,
+    131,1,130,1,124,0,12,0,114,106,124,2,100,2,107,2,
+    114,106,116,5,100,6,131,1,130,1,100,7,83,0,41,8,
+    122,28,86,101,114,105,102,121,32,97,114,103,117,109,101,110,
+    116,115,32,97,114,101,32,34,115,97,110,101,34,46,122,31,
+    109,111,100,117,108,101,32,110,97,109,101,32,109,117,115,116,
+    32,98,101,32,115,116,114,44,32,110,111,116,32,123,125,114,
+    19,0,0,0,122,18,108,101,118,101,108,32,109,117,115,116,
+    32,98,101,32,62,61,32,48,122,31,95,95,112,97,99,107,
+    97,103,101,95,95,32,110,111,116,32,115,101,116,32,116,111,
+    32,97,32,115,116,114,105,110,103,122,54,97,116,116,101,109,
+    112,116,101,100,32,114,101,108,97,116,105,118,101,32,105,109,
+    112,111,114,116,32,119,105,116,104,32,110,111,32,107,110,111,
+    119,110,32,112,97,114,101,110,116,32,112,97,99,107,97,103,
+    101,122,17,69,109,112,116,121,32,109,111,100,117,108,101,32,
+    110,97,109,101,78,41,7,218,10,105,115,105,110,115,116,97,
+    110,99,101,218,3,115,116,114,218,9,84,121,112,101,69,114,
+    114,111,114,114,38,0,0,0,114,13,0,0,0,114,160,0,
+    0,0,114,71,0,0,0,41,3,114,15,0,0,0,114,161,
+    0,0,0,114,162,0,0,0,114,10,0,0,0,114,10,0,
     0,0,114,11,0,0,0,218,13,95,115,97,110,105,116,121,
-    95,99,104,101,99,107,140,3,0,0,115,28,0,0,0,0,
+    95,99,104,101,99,107,149,3,0,0,115,22,0,0,0,0,
     2,10,1,18,1,8,1,8,1,8,1,10,1,10,1,4,
-    1,10,2,10,1,4,2,14,1,14,1,114,175,0,0,0,
-    122,16,78,111,32,109,111,100,117,108,101,32,110,97,109,101,
-    100,32,122,4,123,33,114,125,99,2,0,0,0,0,0,0,
-    0,8,0,0,0,12,0,0,0,67,0,0,0,115,220,0,
-    0,0,100,0,125,2,124,0,106,0,100,1,131,1,100,2,
-    25,0,125,3,124,3,114,134,124,3,116,1,106,2,107,7,
-    114,42,116,3,124,1,124,3,131,2,1,0,124,0,116,1,
-    106,2,107,6,114,62,116,1,106,2,124,0,25,0,83,0,
-    116,1,106,2,124,3,25,0,125,4,121,10,124,4,106,4,
-    125,2,87,0,110,50,4,0,116,5,107,10,114,132,1,0,
-    1,0,1,0,116,6,100,3,23,0,106,7,124,0,124,3,
-    131,2,125,5,116,8,124,5,124,0,100,4,141,2,100,0,
-    130,2,89,0,110,2,88,0,116,9,124,0,124,2,131,2,
-    125,6,124,6,100,0,107,8,114,172,116,8,116,6,106,7,
-    124,0,131,1,124,0,100,4,141,2,130,1,110,8,116,10,
-    124,6,131,1,125,7,124,3,114,216,116,1,106,2,124,3,
-    25,0,125,4,116,11,124,4,124,0,106,0,100,1,131,1,
-    100,5,25,0,124,7,131,3,1,0,124,7,83,0,41,6,
-    78,114,117,0,0,0,114,19,0,0,0,122,23,59,32,123,
-    33,114,125,32,105,115,32,110,111,116,32,97,32,112,97,99,
-    107,97,103,101,41,1,114,15,0,0,0,233,2,0,0,0,
-    41,12,114,118,0,0,0,114,14,0,0,0,114,79,0,0,
-    0,114,58,0,0,0,114,127,0,0,0,114,90,0,0,0,
-    218,8,95,69,82,82,95,77,83,71,114,38,0,0,0,218,
-    19,77,111,100,117,108,101,78,111,116,70,111,117,110,100,69,
-    114,114,111,114,114,170,0,0,0,114,141,0,0,0,114,5,
-    0,0,0,41,8,114,15,0,0,0,218,7,105,109,112,111,
-    114,116,95,114,144,0,0,0,114,119,0,0,0,90,13,112,
-    97,114,101,110,116,95,109,111,100,117,108,101,114,139,0,0,
-    0,114,82,0,0,0,114,83,0,0,0,114,10,0,0,0,
-    114,10,0,0,0,114,11,0,0,0,218,23,95,102,105,110,
-    100,95,97,110,100,95,108,111,97,100,95,117,110,108,111,99,
-    107,101,100,163,3,0,0,115,42,0,0,0,0,1,4,1,
-    14,1,4,1,10,1,10,2,10,1,10,1,10,1,2,1,
-    10,1,14,1,16,1,20,1,10,1,8,1,20,2,8,1,
-    4,2,10,1,22,1,114,180,0,0,0,99,2,0,0,0,
-    0,0,0,0,2,0,0,0,10,0,0,0,67,0,0,0,
-    115,30,0,0,0,116,0,124,0,131,1,143,12,1,0,116,
-    1,124,0,124,1,131,2,83,0,81,0,82,0,88,0,100,
-    1,83,0,41,2,122,54,70,105,110,100,32,97,110,100,32,
-    108,111,97,100,32,116,104,101,32,109,111,100,117,108,101,44,
-    32,97,110,100,32,114,101,108,101,97,115,101,32,116,104,101,
-    32,105,109,112,111,114,116,32,108,111,99,107,46,78,41,2,
-    114,42,0,0,0,114,180,0,0,0,41,2,114,15,0,0,
-    0,114,179,0,0,0,114,10,0,0,0,114,10,0,0,0,
-    114,11,0,0,0,218,14,95,102,105,110,100,95,97,110,100,
-    95,108,111,97,100,190,3,0,0,115,4,0,0,0,0,2,
-    10,1,114,181,0,0,0,114,19,0,0,0,99,3,0,0,
-    0,0,0,0,0,5,0,0,0,4,0,0,0,67,0,0,
-    0,115,120,0,0,0,116,0,124,0,124,1,124,2,131,3,
-    1,0,124,2,100,1,107,4,114,32,116,1,124,0,124,1,
-    124,2,131,3,125,0,116,2,106,3,131,0,1,0,124,0,
-    116,4,106,5,107,7,114,60,116,6,124,0,116,7,131,2,
-    83,0,116,4,106,5,124,0,25,0,125,3,124,3,100,2,
-    107,8,114,108,116,2,106,8,131,0,1,0,100,3,106,9,
-    124,0,131,1,125,4,116,10,124,4,124,0,100,4,141,2,
-    130,1,116,11,124,0,131,1,1,0,124,3,83,0,41,5,
-    97,50,1,0,0,73,109,112,111,114,116,32,97,110,100,32,
-    114,101,116,117,114,110,32,116,104,101,32,109,111,100,117,108,
-    101,32,98,97,115,101,100,32,111,110,32,105,116,115,32,110,
-    97,109,101,44,32,116,104,101,32,112,97,99,107,97,103,101,
-    32,116,104,101,32,99,97,108,108,32,105,115,10,32,32,32,
-    32,98,101,105,110,103,32,109,97,100,101,32,102,114,111,109,
-    44,32,97,110,100,32,116,104,101,32,108,101,118,101,108,32,
-    97,100,106,117,115,116,109,101,110,116,46,10,10,32,32,32,
-    32,84,104,105,115,32,102,117,110,99,116,105,111,110,32,114,
-    101,112,114,101,115,101,110,116,115,32,116,104,101,32,103,114,
-    101,97,116,101,115,116,32,99,111,109,109,111,110,32,100,101,
-    110,111,109,105,110,97,116,111,114,32,111,102,32,102,117,110,
-    99,116,105,111,110,97,108,105,116,121,10,32,32,32,32,98,
-    101,116,119,101,101,110,32,105,109,112,111,114,116,95,109,111,
-    100,117,108,101,32,97,110,100,32,95,95,105,109,112,111,114,
-    116,95,95,46,32,84,104,105,115,32,105,110,99,108,117,100,
-    101,115,32,115,101,116,116,105,110,103,32,95,95,112,97,99,
-    107,97,103,101,95,95,32,105,102,10,32,32,32,32,116,104,
-    101,32,108,111,97,100,101,114,32,100,105,100,32,110,111,116,
-    46,10,10,32,32,32,32,114,19,0,0,0,78,122,40,105,
-    109,112,111,114,116,32,111,102,32,123,125,32,104,97,108,116,
-    101,100,59,32,78,111,110,101,32,105,110,32,115,121,115,46,
-    109,111,100,117,108,101,115,41,1,114,15,0,0,0,41,12,
-    114,175,0,0,0,114,163,0,0,0,114,46,0,0,0,114,
-    137,0,0,0,114,14,0,0,0,114,79,0,0,0,114,181,
-    0,0,0,218,11,95,103,99,100,95,105,109,112,111,114,116,
-    114,47,0,0,0,114,38,0,0,0,114,178,0,0,0,114,
-    56,0,0,0,41,5,114,15,0,0,0,114,161,0,0,0,
-    114,162,0,0,0,114,83,0,0,0,114,67,0,0,0,114,
-    10,0,0,0,114,10,0,0,0,114,11,0,0,0,114,182,
-    0,0,0,196,3,0,0,115,28,0,0,0,0,9,12,1,
-    8,1,12,1,8,1,10,1,10,1,10,1,8,1,8,1,
-    4,1,6,1,12,1,8,1,114,182,0,0,0,99,3,0,
+    1,8,2,14,1,114,174,0,0,0,122,16,78,111,32,109,
+    111,100,117,108,101,32,110,97,109,101,100,32,122,4,123,33,
+    114,125,99,2,0,0,0,0,0,0,0,8,0,0,0,12,
+    0,0,0,67,0,0,0,115,220,0,0,0,100,0,125,2,
+    124,0,106,0,100,1,131,1,100,2,25,0,125,3,124,3,
+    114,134,124,3,116,1,106,2,107,7,114,42,116,3,124,1,
+    124,3,131,2,1,0,124,0,116,1,106,2,107,6,114,62,
+    116,1,106,2,124,0,25,0,83,0,116,1,106,2,124,3,
+    25,0,125,4,121,10,124,4,106,4,125,2,87,0,110,50,
+    4,0,116,5,107,10,114,132,1,0,1,0,1,0,116,6,
+    100,3,23,0,106,7,124,0,124,3,131,2,125,5,116,8,
+    124,5,124,0,100,4,141,2,100,0,130,2,89,0,110,2,
+    88,0,116,9,124,0,124,2,131,2,125,6,124,6,100,0,
+    107,8,114,172,116,8,116,6,106,7,124,0,131,1,124,0,
+    100,4,141,2,130,1,110,8,116,10,124,6,131,1,125,7,
+    124,3,114,216,116,1,106,2,124,3,25,0,125,4,116,11,
+    124,4,124,0,106,0,100,1,131,1,100,5,25,0,124,7,
+    131,3,1,0,124,7,83,0,41,6,78,114,118,0,0,0,
+    114,19,0,0,0,122,23,59,32,123,33,114,125,32,105,115,
+    32,110,111,116,32,97,32,112,97,99,107,97,103,101,41,1,
+    114,15,0,0,0,233,2,0,0,0,41,12,114,119,0,0,
+    0,114,14,0,0,0,114,80,0,0,0,114,59,0,0,0,
+    114,128,0,0,0,114,91,0,0,0,218,8,95,69,82,82,
+    95,77,83,71,114,38,0,0,0,218,19,77,111,100,117,108,
+    101,78,111,116,70,111,117,110,100,69,114,114,111,114,114,170,
+    0,0,0,114,141,0,0,0,114,5,0,0,0,41,8,114,
+    15,0,0,0,218,7,105,109,112,111,114,116,95,114,144,0,
+    0,0,114,120,0,0,0,90,13,112,97,114,101,110,116,95,
+    109,111,100,117,108,101,114,139,0,0,0,114,83,0,0,0,
+    114,84,0,0,0,114,10,0,0,0,114,10,0,0,0,114,
+    11,0,0,0,218,23,95,102,105,110,100,95,97,110,100,95,
+    108,111,97,100,95,117,110,108,111,99,107,101,100,168,3,0,
+    0,115,42,0,0,0,0,1,4,1,14,1,4,1,10,1,
+    10,2,10,1,10,1,10,1,2,1,10,1,14,1,16,1,
+    20,1,10,1,8,1,20,2,8,1,4,2,10,1,22,1,
+    114,179,0,0,0,99,2,0,0,0,0,0,0,0,4,0,
+    0,0,11,0,0,0,67,0,0,0,115,94,0,0,0,116,
+    0,124,0,131,1,143,38,1,0,116,1,106,2,106,3,124,
+    0,116,4,131,2,125,2,124,2,116,4,107,8,114,42,116,
+    5,124,0,124,1,131,2,83,0,87,0,100,1,81,0,82,
+    0,88,0,124,2,100,1,107,8,114,82,100,2,106,6,124,
+    0,131,1,125,3,116,7,124,3,124,0,100,3,141,2,130,
+    1,116,8,124,0,131,1,1,0,124,2,83,0,41,4,122,
+    25,70,105,110,100,32,97,110,100,32,108,111,97,100,32,116,
+    104,101,32,109,111,100,117,108,101,46,78,122,40,105,109,112,
+    111,114,116,32,111,102,32,123,125,32,104,97,108,116,101,100,
+    59,32,78,111,110,101,32,105,110,32,115,121,115,46,109,111,
+    100,117,108,101,115,41,1,114,15,0,0,0,41,9,114,42,
+    0,0,0,114,14,0,0,0,114,80,0,0,0,114,30,0,
+    0,0,218,14,95,78,69,69,68,83,95,76,79,65,68,73,
+    78,71,114,179,0,0,0,114,38,0,0,0,114,177,0,0,
+    0,114,57,0,0,0,41,4,114,15,0,0,0,114,178,0,
+    0,0,114,84,0,0,0,114,68,0,0,0,114,10,0,0,
+    0,114,10,0,0,0,114,11,0,0,0,218,14,95,102,105,
+    110,100,95,97,110,100,95,108,111,97,100,198,3,0,0,115,
+    20,0,0,0,0,2,10,1,14,1,8,1,20,2,8,1,
+    4,1,6,1,12,2,8,1,114,181,0,0,0,114,19,0,
+    0,0,99,3,0,0,0,0,0,0,0,3,0,0,0,4,
+    0,0,0,67,0,0,0,115,42,0,0,0,116,0,124,0,
+    124,1,124,2,131,3,1,0,124,2,100,1,107,4,114,32,
+    116,1,124,0,124,1,124,2,131,3,125,0,116,2,124,0,
+    116,3,131,2,83,0,41,2,97,50,1,0,0,73,109,112,
+    111,114,116,32,97,110,100,32,114,101,116,117,114,110,32,116,
+    104,101,32,109,111,100,117,108,101,32,98,97,115,101,100,32,
+    111,110,32,105,116,115,32,110,97,109,101,44,32,116,104,101,
+    32,112,97,99,107,97,103,101,32,116,104,101,32,99,97,108,
+    108,32,105,115,10,32,32,32,32,98,101,105,110,103,32,109,
+    97,100,101,32,102,114,111,109,44,32,97,110,100,32,116,104,
+    101,32,108,101,118,101,108,32,97,100,106,117,115,116,109,101,
+    110,116,46,10,10,32,32,32,32,84,104,105,115,32,102,117,
+    110,99,116,105,111,110,32,114,101,112,114,101,115,101,110,116,
+    115,32,116,104,101,32,103,114,101,97,116,101,115,116,32,99,
+    111,109,109,111,110,32,100,101,110,111,109,105,110,97,116,111,
+    114,32,111,102,32,102,117,110,99,116,105,111,110,97,108,105,
+    116,121,10,32,32,32,32,98,101,116,119,101,101,110,32,105,
+    109,112,111,114,116,95,109,111,100,117,108,101,32,97,110,100,
+    32,95,95,105,109,112,111,114,116,95,95,46,32,84,104,105,
+    115,32,105,110,99,108,117,100,101,115,32,115,101,116,116,105,
+    110,103,32,95,95,112,97,99,107,97,103,101,95,95,32,105,
+    102,10,32,32,32,32,116,104,101,32,108,111,97,100,101,114,
+    32,100,105,100,32,110,111,116,46,10,10,32,32,32,32,114,
+    19,0,0,0,41,4,114,174,0,0,0,114,163,0,0,0,
+    114,181,0,0,0,218,11,95,103,99,100,95,105,109,112,111,
+    114,116,41,3,114,15,0,0,0,114,161,0,0,0,114,162,
+    0,0,0,114,10,0,0,0,114,10,0,0,0,114,11,0,
+    0,0,114,182,0,0,0,214,3,0,0,115,8,0,0,0,
+    0,9,12,1,8,1,12,1,114,182,0,0,0,99,3,0,
     0,0,0,0,0,0,6,0,0,0,17,0,0,0,67,0,
     0,0,115,164,0,0,0,116,0,124,0,100,1,131,2,114,
     160,100,2,124,1,107,6,114,58,116,1,124,1,131,1,125,
@@ -1601,17 +1588,17 @@ const unsigned char _Py_M__importlib[] = {
     117,109,105,110,103,32,105,109,112,111,114,116,108,105,98,39,
     115,10,32,32,32,32,105,109,112,111,114,116,32,105,109,112,
     108,101,109,101,110,116,97,116,105,111,110,32,105,115,32,100,
-    101,115,105,114,101,100,46,10,10,32,32,32,32,114,127,0,
+    101,115,105,114,101,100,46,10,10,32,32,32,32,114,128,0,
     0,0,250,1,42,218,7,95,95,97,108,108,95,95,122,5,
-    123,125,46,123,125,78,41,10,114,4,0,0,0,114,126,0,
+    123,125,46,123,125,78,41,10,114,4,0,0,0,114,127,0,
     0,0,218,6,114,101,109,111,118,101,218,6,101,120,116,101,
     110,100,114,184,0,0,0,114,38,0,0,0,114,1,0,0,
-    0,114,58,0,0,0,114,178,0,0,0,114,15,0,0,0,
-    41,6,114,83,0,0,0,218,8,102,114,111,109,108,105,115,
-    116,114,179,0,0,0,218,1,120,90,9,102,114,111,109,95,
+    0,114,59,0,0,0,114,177,0,0,0,114,15,0,0,0,
+    41,6,114,84,0,0,0,218,8,102,114,111,109,108,105,115,
+    116,114,178,0,0,0,218,1,120,90,9,102,114,111,109,95,
     110,97,109,101,90,3,101,120,99,114,10,0,0,0,114,10,
     0,0,0,114,11,0,0,0,218,16,95,104,97,110,100,108,
-    101,95,102,114,111,109,108,105,115,116,221,3,0,0,115,32,
+    101,95,102,114,111,109,108,105,115,116,229,3,0,0,115,32,
     0,0,0,0,10,10,1,8,1,8,1,10,1,10,1,12,
     1,10,1,10,1,14,1,2,1,14,1,16,4,10,1,2,
     1,24,1,114,189,0,0,0,99,1,0,0,0,0,0,0,
@@ -1636,7 +1623,7 @@ const unsigned char _Py_M__importlib[] = {
     112,114,101,115,101,110,116,32,116,104,97,116,32,105,116,115,
     32,112,114,111,112,101,114,32,118,97,108,117,101,32,105,115,
     32,117,110,107,110,111,119,110,46,10,10,32,32,32,32,114,
-    130,0,0,0,114,89,0,0,0,78,122,32,95,95,112,97,
+    131,0,0,0,114,90,0,0,0,78,122,32,95,95,112,97,
     99,107,97,103,101,95,95,32,33,61,32,95,95,115,112,101,
     99,95,95,46,112,97,114,101,110,116,32,40,122,4,32,33,
     61,32,250,1,41,233,3,0,0,0,41,1,90,10,115,116,
@@ -1646,13 +1633,13 @@ const unsigned char _Py_M__importlib[] = {
     32,95,95,112,97,99,107,97,103,101,95,95,44,32,102,97,
     108,108,105,110,103,32,98,97,99,107,32,111,110,32,95,95,
     110,97,109,101,95,95,32,97,110,100,32,95,95,112,97,116,
-    104,95,95,114,1,0,0,0,114,127,0,0,0,114,117,0,
-    0,0,114,19,0,0,0,41,6,114,30,0,0,0,114,119,
+    104,95,95,114,1,0,0,0,114,128,0,0,0,114,118,0,
+    0,0,114,19,0,0,0,41,6,114,30,0,0,0,114,120,
     0,0,0,114,167,0,0,0,114,168,0,0,0,114,169,0,
-    0,0,114,118,0,0,0,41,3,218,7,103,108,111,98,97,
-    108,115,114,161,0,0,0,114,82,0,0,0,114,10,0,0,
+    0,0,114,119,0,0,0,41,3,218,7,103,108,111,98,97,
+    108,115,114,161,0,0,0,114,83,0,0,0,114,10,0,0,
     0,114,10,0,0,0,114,11,0,0,0,218,17,95,99,97,
-    108,99,95,95,95,112,97,99,107,97,103,101,95,95,252,3,
+    108,99,95,95,95,112,97,99,107,97,103,101,95,95,4,4,
     0,0,115,30,0,0,0,0,7,10,1,10,1,8,1,18,
     1,22,2,10,1,4,1,8,1,6,2,6,2,10,1,8,
     1,8,1,14,1,114,193,0,0,0,99,5,0,0,0,0,
@@ -1697,16 +1684,16 @@ const unsigned char _Py_M__importlib[] = {
     114,111,109,32,46,46,112,107,103,32,105,109,112,111,114,116,
     32,109,111,100,96,96,32,119,111,117,108,100,32,104,97,118,
     101,32,97,32,39,108,101,118,101,108,39,32,111,102,32,50,
-    41,46,10,10,32,32,32,32,114,19,0,0,0,78,114,117,
+    41,46,10,10,32,32,32,32,114,19,0,0,0,78,114,118,
     0,0,0,41,8,114,182,0,0,0,114,193,0,0,0,218,
     9,112,97,114,116,105,116,105,111,110,114,159,0,0,0,114,
-    14,0,0,0,114,79,0,0,0,114,1,0,0,0,114,189,
+    14,0,0,0,114,80,0,0,0,114,1,0,0,0,114,189,
     0,0,0,41,9,114,15,0,0,0,114,192,0,0,0,218,
     6,108,111,99,97,108,115,114,187,0,0,0,114,162,0,0,
-    0,114,83,0,0,0,90,8,103,108,111,98,97,108,115,95,
+    0,114,84,0,0,0,90,8,103,108,111,98,97,108,115,95,
     114,161,0,0,0,90,7,99,117,116,95,111,102,102,114,10,
     0,0,0,114,10,0,0,0,114,11,0,0,0,218,10,95,
-    95,105,109,112,111,114,116,95,95,23,4,0,0,115,26,0,
+    95,105,109,112,111,114,116,95,95,31,4,0,0,115,26,0,
     0,0,0,11,8,1,10,2,16,1,8,1,12,1,4,3,
     8,1,18,1,4,1,4,4,26,3,32,2,114,196,0,0,
     0,99,1,0,0,0,0,0,0,0,2,0,0,0,3,0,
@@ -1715,11 +1702,11 @@ const unsigned char _Py_M__importlib[] = {
     1,124,0,23,0,131,1,130,1,116,3,124,1,131,1,83,
     0,41,2,78,122,25,110,111,32,98,117,105,108,116,45,105,
     110,32,109,111,100,117,108,101,32,110,97,109,101,100,32,41,
-    4,114,142,0,0,0,114,146,0,0,0,114,70,0,0,0,
-    114,141,0,0,0,41,2,114,15,0,0,0,114,82,0,0,
+    4,114,142,0,0,0,114,146,0,0,0,114,71,0,0,0,
+    114,141,0,0,0,41,2,114,15,0,0,0,114,83,0,0,
     0,114,10,0,0,0,114,10,0,0,0,114,11,0,0,0,
     218,18,95,98,117,105,108,116,105,110,95,102,114,111,109,95,
-    110,97,109,101,58,4,0,0,115,8,0,0,0,0,1,10,
+    110,97,109,101,66,4,0,0,115,8,0,0,0,0,1,10,
     1,8,1,12,1,114,197,0,0,0,99,2,0,0,0,0,
     0,0,0,12,0,0,0,12,0,0,0,67,0,0,0,115,
     244,0,0,0,124,1,97,0,124,0,97,1,116,2,116,1,
@@ -1754,22 +1741,22 @@ const unsigned char _Py_M__importlib[] = {
     101,115,32,109,117,115,116,32,98,101,32,101,120,112,108,105,
     99,105,116,108,121,32,112,97,115,115,101,100,32,105,110,46,
     10,10,32,32,32,32,114,167,0,0,0,114,20,0,0,0,
-    78,114,55,0,0,0,41,1,114,167,0,0,0,41,16,114,
-    46,0,0,0,114,14,0,0,0,114,13,0,0,0,114,79,
+    78,114,56,0,0,0,41,1,114,167,0,0,0,41,16,114,
+    49,0,0,0,114,14,0,0,0,114,13,0,0,0,114,80,
     0,0,0,218,5,105,116,101,109,115,114,171,0,0,0,114,
-    69,0,0,0,114,142,0,0,0,114,75,0,0,0,114,152,
-    0,0,0,114,128,0,0,0,114,133,0,0,0,114,1,0,
-    0,0,114,197,0,0,0,114,5,0,0,0,114,70,0,0,
+    70,0,0,0,114,142,0,0,0,114,76,0,0,0,114,152,
+    0,0,0,114,129,0,0,0,114,134,0,0,0,114,1,0,
+    0,0,114,197,0,0,0,114,5,0,0,0,114,71,0,0,
     0,41,12,218,10,115,121,115,95,109,111,100,117,108,101,218,
     11,95,105,109,112,95,109,111,100,117,108,101,90,11,109,111,
-    100,117,108,101,95,116,121,112,101,114,15,0,0,0,114,83,
-    0,0,0,114,93,0,0,0,114,82,0,0,0,90,11,115,
+    100,117,108,101,95,116,121,112,101,114,15,0,0,0,114,84,
+    0,0,0,114,94,0,0,0,114,83,0,0,0,90,11,115,
     101,108,102,95,109,111,100,117,108,101,90,12,98,117,105,108,
     116,105,110,95,110,97,109,101,90,14,98,117,105,108,116,105,
     110,95,109,111,100,117,108,101,90,13,116,104,114,101,97,100,
     95,109,111,100,117,108,101,90,14,119,101,97,107,114,101,102,
     95,109,111,100,117,108,101,114,10,0,0,0,114,10,0,0,
-    0,114,11,0,0,0,218,6,95,115,101,116,117,112,65,4,
+    0,114,11,0,0,0,218,6,95,115,101,116,117,112,73,4,
     0,0,115,50,0,0,0,0,9,4,1,4,3,8,1,20,
     1,10,1,10,1,6,1,10,1,6,2,2,1,10,1,14,
     3,10,1,10,1,10,1,10,2,10,1,16,3,2,1,12,
@@ -1784,39 +1771,39 @@ const unsigned char _Py_M__importlib[] = {
     97,115,32,116,104,101,32,105,109,112,108,101,109,101,110,116,
     97,116,105,111,110,32,111,102,32,105,109,112,111,114,116,46,
     114,19,0,0,0,78,41,11,114,201,0,0,0,114,14,0,
-    0,0,114,166,0,0,0,114,109,0,0,0,114,142,0,0,
+    0,0,114,166,0,0,0,114,110,0,0,0,114,142,0,0,
     0,114,152,0,0,0,218,26,95,102,114,111,122,101,110,95,
     105,109,112,111,114,116,108,105,98,95,101,120,116,101,114,110,
-    97,108,114,115,0,0,0,218,8,95,105,110,115,116,97,108,
-    108,114,79,0,0,0,114,1,0,0,0,41,3,114,199,0,
+    97,108,114,116,0,0,0,218,8,95,105,110,115,116,97,108,
+    108,114,80,0,0,0,114,1,0,0,0,41,3,114,199,0,
     0,0,114,200,0,0,0,114,202,0,0,0,114,10,0,0,
     0,114,10,0,0,0,114,11,0,0,0,114,203,0,0,0,
-    112,4,0,0,115,12,0,0,0,0,2,10,2,12,1,12,
+    120,4,0,0,115,12,0,0,0,0,2,10,2,12,1,12,
     3,8,1,4,1,114,203,0,0,0,41,2,78,78,41,1,
     78,41,2,78,114,19,0,0,0,41,50,114,3,0,0,0,
-    114,115,0,0,0,114,12,0,0,0,114,16,0,0,0,114,
+    114,116,0,0,0,114,12,0,0,0,114,16,0,0,0,114,
     51,0,0,0,114,29,0,0,0,114,36,0,0,0,114,17,
     0,0,0,114,18,0,0,0,114,41,0,0,0,114,42,0,
-    0,0,114,45,0,0,0,114,56,0,0,0,114,58,0,0,
-    0,114,68,0,0,0,114,74,0,0,0,114,77,0,0,0,
-    114,84,0,0,0,114,95,0,0,0,114,96,0,0,0,114,
-    102,0,0,0,114,78,0,0,0,218,6,111,98,106,101,99,
-    116,90,9,95,80,79,80,85,76,65,84,69,114,128,0,0,
-    0,114,133,0,0,0,114,136,0,0,0,114,91,0,0,0,
-    114,80,0,0,0,114,140,0,0,0,114,141,0,0,0,114,
-    81,0,0,0,114,142,0,0,0,114,152,0,0,0,114,157,
-    0,0,0,114,163,0,0,0,114,165,0,0,0,114,170,0,
-    0,0,114,175,0,0,0,90,15,95,69,82,82,95,77,83,
-    71,95,80,82,69,70,73,88,114,177,0,0,0,114,180,0,
-    0,0,114,181,0,0,0,114,182,0,0,0,114,189,0,0,
-    0,114,193,0,0,0,114,196,0,0,0,114,197,0,0,0,
-    114,201,0,0,0,114,203,0,0,0,114,10,0,0,0,114,
-    10,0,0,0,114,10,0,0,0,114,11,0,0,0,218,8,
-    60,109,111,100,117,108,101,62,8,0,0,0,115,94,0,0,
-    0,4,17,4,2,8,8,8,7,4,2,4,3,16,4,14,
-    68,14,21,14,19,8,19,8,19,8,11,14,8,8,11,8,
-    12,8,16,8,36,14,27,14,101,16,26,6,3,10,45,14,
-    60,8,17,8,17,8,25,8,29,8,23,8,16,14,73,14,
-    77,14,13,8,9,8,9,10,47,8,20,4,1,8,2,8,
-    27,8,6,10,25,8,31,8,27,18,35,8,7,8,47,
+    0,0,114,45,0,0,0,114,57,0,0,0,114,59,0,0,
+    0,114,69,0,0,0,114,75,0,0,0,114,78,0,0,0,
+    114,85,0,0,0,114,96,0,0,0,114,97,0,0,0,114,
+    103,0,0,0,114,79,0,0,0,114,129,0,0,0,114,134,
+    0,0,0,114,137,0,0,0,114,92,0,0,0,114,81,0,
+    0,0,114,140,0,0,0,114,141,0,0,0,114,82,0,0,
+    0,114,142,0,0,0,114,152,0,0,0,114,157,0,0,0,
+    114,163,0,0,0,114,165,0,0,0,114,170,0,0,0,114,
+    174,0,0,0,90,15,95,69,82,82,95,77,83,71,95,80,
+    82,69,70,73,88,114,176,0,0,0,114,179,0,0,0,218,
+    6,111,98,106,101,99,116,114,180,0,0,0,114,181,0,0,
+    0,114,182,0,0,0,114,189,0,0,0,114,193,0,0,0,
+    114,196,0,0,0,114,197,0,0,0,114,201,0,0,0,114,
+    203,0,0,0,114,10,0,0,0,114,10,0,0,0,114,10,
+    0,0,0,114,11,0,0,0,218,8,60,109,111,100,117,108,
+    101,62,8,0,0,0,115,94,0,0,0,4,17,4,2,8,
+    8,8,8,4,2,4,3,16,4,14,68,14,21,14,16,8,
+    37,8,17,8,11,14,8,8,11,8,12,8,16,8,36,14,
+    27,14,101,16,26,10,45,14,60,8,17,8,17,8,24,8,
+    29,8,23,8,15,14,73,14,77,14,13,8,9,8,9,10,
+    47,8,16,4,1,8,2,8,27,6,3,8,16,10,15,8,
+    31,8,27,18,35,8,7,8,47,
 };
index 7b12ab7..22ca49c 100644 (file)
@@ -1105,6 +1105,7 @@ r_object(RFILE *p)
 
     case TYPE_ASCII_INTERNED:
         is_interned = 1;
+        /* fall through */
     case TYPE_ASCII:
         n = r_long(p);
         if (PyErr_Occurred())
@@ -1117,6 +1118,7 @@ r_object(RFILE *p)
 
     case TYPE_SHORT_ASCII_INTERNED:
         is_interned = 1;
+        /* fall through */
     case TYPE_SHORT_ASCII:
         n = r_byte(p);
         if (n == EOF) {
@@ -1142,6 +1144,7 @@ r_object(RFILE *p)
 
     case TYPE_INTERNED:
         is_interned = 1;
+        /* fall through */
     case TYPE_UNICODE:
         {
         const char *buffer;
index 57a2da7..a2ec230 100644 (file)
@@ -393,13 +393,13 @@ siphash24(const void *src, Py_ssize_t src_sz) {
     pt = (uint8_t *)&t;
     m = (uint8_t *)in;
     switch (src_sz) {
-        case 7: pt[6] = m[6];
-        case 6: pt[5] = m[5];
-        case 5: pt[4] = m[4];
+        case 7: pt[6] = m[6]; /* fall through */
+        case 6: pt[5] = m[5]; /* fall through */
+        case 5: pt[4] = m[4]; /* fall through */
         case 4: memcpy(pt, m, sizeof(uint32_t)); break;
-        case 3: pt[2] = m[2];
-        case 2: pt[1] = m[1];
-        case 1: pt[0] = m[0];
+        case 3: pt[2] = m[2]; /* fall through */
+        case 2: pt[1] = m[1]; /* fall through */
+        case 1: pt[0] = m[0]; /* fall through */
     }
     b |= _le64toh(t);
 
index 3015a6b..b416eff 100644 (file)
@@ -97,7 +97,7 @@ static int
 _PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator,
                             double denominator, _PyTime_round_t round)
 {
-    double intpart, err;
+    double intpart;
     /* volatile avoids optimization changing how numbers are rounded */
     volatile double floatpart;
 
@@ -115,14 +115,13 @@ _PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator,
     }
     assert(0.0 <= floatpart && floatpart < denominator);
 
-    *sec = (time_t)intpart;
-    *numerator = (long)floatpart;
-
-    err = intpart - (double)*sec;
-    if (err <= -1.0 || err >= 1.0) {
+    if (!_Py_InIntegralTypeRange(time_t, intpart)) {
         error_time_t_overflow();
         return -1;
     }
+    *sec = (time_t)intpart;
+    *numerator = (long)floatpart;
+
     return 0;
 }
 
@@ -134,6 +133,11 @@ _PyTime_ObjectToDenominator(PyObject *obj, time_t *sec, long *numerator,
 
     if (PyFloat_Check(obj)) {
         double d = PyFloat_AsDouble(obj);
+        if (Py_IS_NAN(d)) {
+            *numerator = 0;
+            PyErr_SetString(PyExc_ValueError, "Invalid value NaN (not a number)");
+            return -1;
+        }
         return _PyTime_DoubleToDenominator(d, sec, numerator,
                                            denominator, round);
     }
@@ -150,20 +154,24 @@ int
 _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round)
 {
     if (PyFloat_Check(obj)) {
-        double intpart, err;
+        double intpart;
         /* volatile avoids optimization changing how numbers are rounded */
         volatile double d;
 
         d = PyFloat_AsDouble(obj);
+        if (Py_IS_NAN(d)) {
+            PyErr_SetString(PyExc_ValueError, "Invalid value NaN (not a number)");
+            return -1;
+        }
+
         d = _PyTime_Round(d, round);
         (void)modf(d, &intpart);
 
-        *sec = (time_t)intpart;
-        err = intpart - (double)*sec;
-        if (err <= -1.0 || err >= 1.0) {
+        if (!_Py_InIntegralTypeRange(time_t, intpart)) {
             error_time_t_overflow();
             return -1;
         }
+        *sec = (time_t)intpart;
         return 0;
     }
     else {
@@ -180,7 +188,9 @@ _PyTime_ObjectToTimespec(PyObject *obj, time_t *sec, long *nsec,
 {
     int res;
     res = _PyTime_ObjectToDenominator(obj, sec, nsec, 1e9, round);
-    assert(0 <= *nsec && *nsec < SEC_TO_NS);
+    if (res == 0) {
+        assert(0 <= *nsec && *nsec < SEC_TO_NS);
+    }
     return res;
 }
 
@@ -190,7 +200,9 @@ _PyTime_ObjectToTimeval(PyObject *obj, time_t *sec, long *usec,
 {
     int res;
     res = _PyTime_ObjectToDenominator(obj, sec, usec, 1e6, round);
-    assert(0 <= *usec && *usec < SEC_TO_US);
+    if (res == 0) {
+        assert(0 <= *usec && *usec < SEC_TO_US);
+    }
     return res;
 }
 
@@ -276,7 +288,6 @@ static int
 _PyTime_FromFloatObject(_PyTime_t *t, double value, _PyTime_round_t round,
                         long unit_to_ns)
 {
-    double err;
     /* volatile avoids optimization changing how numbers are rounded */
     volatile double d;
 
@@ -285,12 +296,11 @@ _PyTime_FromFloatObject(_PyTime_t *t, double value, _PyTime_round_t round,
     d *= (double)unit_to_ns;
     d = _PyTime_Round(d, round);
 
-    *t = (_PyTime_t)d;
-    err = d - (double)*t;
-    if (fabs(err) >= 1.0) {
+    if (!_Py_InIntegralTypeRange(_PyTime_t, d)) {
         _PyTime_overflow();
         return -1;
     }
+    *t = (_PyTime_t)d;
     return 0;
 }
 
@@ -301,6 +311,10 @@ _PyTime_FromObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round,
     if (PyFloat_Check(obj)) {
         double d;
         d = PyFloat_AsDouble(obj);
+        if (Py_IS_NAN(d)) {
+            PyErr_SetString(PyExc_ValueError, "Invalid value NaN (not a number)");
+            return -1;
+        }
         return _PyTime_FromFloatObject(t, d, round, unit_to_ns);
     }
     else {
index ba7393f..3607341 100644 (file)
@@ -466,61 +466,66 @@ PyLockStatus
 PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
                             int intr_flag)
 {
-    PyLockStatus success;
+    PyLockStatus success = PY_LOCK_FAILURE;
     pthread_lock *thelock = (pthread_lock *)lock;
     int status, error = 0;
 
     dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) called\n",
              lock, microseconds, intr_flag));
 
-    status = pthread_mutex_lock( &thelock->mut );
-    CHECK_STATUS_PTHREAD("pthread_mutex_lock[1]");
-
-    if (thelock->locked == 0) {
-        success = PY_LOCK_ACQUIRED;
-    } else if (microseconds == 0) {
-        success = PY_LOCK_FAILURE;
-    } else {
-        struct timespec ts;
-        if (microseconds > 0)
-            MICROSECONDS_TO_TIMESPEC(microseconds, ts);
-        /* continue trying until we get the lock */
-
-        /* mut must be locked by me -- part of the condition
-         * protocol */
-        success = PY_LOCK_FAILURE;
-        while (success == PY_LOCK_FAILURE) {
-            if (microseconds > 0) {
-                status = pthread_cond_timedwait(
-                    &thelock->lock_released,
-                    &thelock->mut, &ts);
-                if (status == ETIMEDOUT)
+    if (microseconds == 0) {
+        status = pthread_mutex_trylock( &thelock->mut );
+        if (status != EBUSY)
+            CHECK_STATUS_PTHREAD("pthread_mutex_trylock[1]");
+    }
+    else {
+        status = pthread_mutex_lock( &thelock->mut );
+        CHECK_STATUS_PTHREAD("pthread_mutex_lock[1]");
+    }
+    if (status == 0) {
+        if (thelock->locked == 0) {
+            success = PY_LOCK_ACQUIRED;
+        }
+        else if (microseconds != 0) {
+            struct timespec ts;
+            if (microseconds > 0)
+                MICROSECONDS_TO_TIMESPEC(microseconds, ts);
+            /* continue trying until we get the lock */
+
+            /* mut must be locked by me -- part of the condition
+             * protocol */
+            while (success == PY_LOCK_FAILURE) {
+                if (microseconds > 0) {
+                    status = pthread_cond_timedwait(
+                        &thelock->lock_released,
+                        &thelock->mut, &ts);
+                    if (status == ETIMEDOUT)
+                        break;
+                    CHECK_STATUS_PTHREAD("pthread_cond_timed_wait");
+                }
+                else {
+                    status = pthread_cond_wait(
+                        &thelock->lock_released,
+                        &thelock->mut);
+                    CHECK_STATUS_PTHREAD("pthread_cond_wait");
+                }
+
+                if (intr_flag && status == 0 && thelock->locked) {
+                    /* We were woken up, but didn't get the lock.  We probably received
+                     * a signal.  Return PY_LOCK_INTR to allow the caller to handle
+                     * it and retry.  */
+                    success = PY_LOCK_INTR;
                     break;
-                CHECK_STATUS_PTHREAD("pthread_cond_timed_wait");
-            }
-            else {
-                status = pthread_cond_wait(
-                    &thelock->lock_released,
-                    &thelock->mut);
-                CHECK_STATUS_PTHREAD("pthread_cond_wait");
-            }
-
-            if (intr_flag && status == 0 && thelock->locked) {
-                /* We were woken up, but didn't get the lock.  We probably received
-                 * a signal.  Return PY_LOCK_INTR to allow the caller to handle
-                 * it and retry.  */
-                success = PY_LOCK_INTR;
-                break;
-            } else if (status == 0 && !thelock->locked) {
-                success = PY_LOCK_ACQUIRED;
-            } else {
-                success = PY_LOCK_FAILURE;
+                }
+                else if (status == 0 && !thelock->locked) {
+                    success = PY_LOCK_ACQUIRED;
+                }
             }
         }
+        if (success == PY_LOCK_ACQUIRED) thelock->locked = 1;
+        status = pthread_mutex_unlock( &thelock->mut );
+        CHECK_STATUS_PTHREAD("pthread_mutex_unlock[1]");
     }
-    if (success == PY_LOCK_ACQUIRED) thelock->locked = 1;
-    status = pthread_mutex_unlock( &thelock->mut );
-    CHECK_STATUS_PTHREAD("pthread_mutex_unlock[1]");
 
     if (error) success = PY_LOCK_FAILURE;
     dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) -> %d\n",
index b0e3a91..cce81c1 100644 (file)
@@ -28,10 +28,13 @@ write_op_arg(_Py_CODEUNIT *codestr, unsigned char opcode,
     switch (ilen) {
         case 4:
             *codestr++ = PACKOPARG(EXTENDED_ARG, (oparg >> 24) & 0xff);
+            /* fall through */
         case 3:
             *codestr++ = PACKOPARG(EXTENDED_ARG, (oparg >> 16) & 0xff);
+            /* fall through */
         case 2:
             *codestr++ = PACKOPARG(EXTENDED_ARG, (oparg >> 8) & 0xff);
+            /* fall through */
         case 1:
             *codestr++ = PACKOPARG(opcode, oparg & 0xff);
             break;
index c43cd44..88a7200 100644 (file)
@@ -1,4 +1,4 @@
-This is Python version 3.6.2
+This is Python version 3.6.3
 ============================
 
 .. image:: https://travis-ci.org/python/cpython.svg?branch=3.6
@@ -26,7 +26,7 @@ General Information
 - Source code: https://github.com/python/cpython
 - Issue tracker: https://bugs.python.org
 - Documentation: https://docs.python.org
-- Developer's Guide: https://docs.python.org/devguide/
+- Developer's Guide: https://devguide.python.org/
 
 Contributing to CPython
 -----------------------
@@ -34,7 +34,7 @@ Contributing to CPython
 For more complete instructions on contributing to CPython development,
 see the `Developer Guide`_.
 
-.. _Developer Guide: https://docs.python.org/devguide/
+.. _Developer Guide: https://devguide.python.org/
 
 Using Python
 ------------
@@ -127,7 +127,7 @@ What's New
 We have a comprehensive overview of the changes in the `What's New in Python
 3.6 <https://docs.python.org/3.6/whatsnew/3.6.html>`_ document.  For a more
 detailed change log, read `Misc/NEWS
-<https://github.com/python/cpython/blob/3.6/Misc/NEWS>`_, but a full
+<https://github.com/python/cpython/blob/3.6/Misc/NEWS.d>`_, but a full
 accounting of changes can only be gleaned from the `commit history
 <https://github.com/python/cpython/commits/3.6>`_.
 
@@ -232,7 +232,8 @@ Copyright and License Information
 ---------------------------------
 
 Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,
-2012, 2013, 2014, 2015, 2016 Python Software Foundation.  All rights reserved.
+2012, 2013, 2014, 2015, 2016, 2017 Python Software Foundation.  All rights
+reserved.
 
 Copyright (c) 2000 BeOpen.com.  All rights reserved.
 
index 31ae811..aac9b5c 100755 (executable)
@@ -99,6 +99,8 @@ hexdigits = "0123456789abcdef"
 
 ENCODING = locale.getpreferredencoding()
 
+EVALFRAME = '_PyEval_EvalFrameDefault'
+
 class NullPyObjectPtr(RuntimeError):
     pass
 
@@ -1492,18 +1494,18 @@ class Frame(object):
     #   - everything else
 
     def is_python_frame(self):
-        '''Is this a PyEval_EvalFrameEx frame, or some other important
+        '''Is this a _PyEval_EvalFrameDefault frame, or some other important
         frame? (see is_other_python_frame for what "important" means in this
         context)'''
-        if self.is_evalframeex():
+        if self.is_evalframe():
             return True
         if self.is_other_python_frame():
             return True
         return False
 
-    def is_evalframeex(self):
-        '''Is this a PyEval_EvalFrameEx frame?'''
-        if self._gdbframe.name() == 'PyEval_EvalFrameEx':
+    def is_evalframe(self):
+        '''Is this a _PyEval_EvalFrameDefault frame?'''
+        if self._gdbframe.name() == EVALFRAME:
             '''
             I believe we also need to filter on the inline
             struct frame_id.inline_depth, only regarding frames with
@@ -1512,7 +1514,7 @@ class Frame(object):
             So we reject those with type gdb.INLINE_FRAME
             '''
             if self._gdbframe.type() == gdb.NORMAL_FRAME:
-                # We have a PyEval_EvalFrameEx frame:
+                # We have a _PyEval_EvalFrameDefault frame:
                 return True
 
         return False
@@ -1631,7 +1633,7 @@ class Frame(object):
         frame = cls.get_selected_frame()
 
         while frame:
-            if frame.is_evalframeex():
+            if frame.is_evalframe():
                 return frame
             frame = frame.older()
 
@@ -1639,7 +1641,7 @@ class Frame(object):
         return None
 
     def print_summary(self):
-        if self.is_evalframeex():
+        if self.is_evalframe():
             pyop = self.get_pyop()
             if pyop:
                 line = pyop.get_truncated_repr(MAX_OUTPUT_LEN)
@@ -1658,7 +1660,7 @@ class Frame(object):
                 sys.stdout.write('#%i\n' % self.get_index())
 
     def print_traceback(self):
-        if self.is_evalframeex():
+        if self.is_evalframe():
             pyop = self.get_pyop()
             if pyop:
                 pyop.print_traceback()
index d4500bc..6bcfd58 100644 (file)
@@ -29,6 +29,7 @@ set DOWNLOAD_URL=https://www.python.org/ftp/python/{version}/{arch}{releasename}
 \r
 set D=%~dp0\r
 set PCBUILD=%D%..\..\PCBuild\\r
+if "%Py_OutDir%"=="" set Py_OutDir=%PCBUILD%\r
 set EXTERNALS=%D%..\..\externals\windows-installer\\r
 \r
 set BUILDX86=\r
@@ -76,9 +77,6 @@ if ERRORLEVEL 1 (echo Cannot locate MSBuild.exe on PATH or as MSBUILD variable &
 if "%SKIPBUILD%" EQU "1" goto skipdoc\r
 if "%SKIPDOC%" EQU "1" goto skipdoc\r
 \r
-if not defined PYTHON where py -q || echo Cannot find py on path and PYTHON is not set. && exit /B 1\r
-if not defined SPHINXBUILD where sphinx-build -q || echo Cannot find sphinx-build on path and SPHINXBUILD is not set. && exit /B 1\r
-\r
 call "%D%..\..\doc\make.bat" htmlhelp\r
 if errorlevel 1 goto :eof\r
 :skipdoc\r
@@ -113,12 +111,12 @@ exit /B 0
 \r
 if "%1" EQU "x86" (\r
     set PGO=\r
-    set BUILD=%PCBUILD%win32\\r
+    set BUILD=%Py_OutDir%win32\\r
     set BUILD_PLAT=Win32\r
     set OUTDIR_PLAT=win32\r
     set OBJDIR_PLAT=x86\r
 ) else (\r
-    set BUILD=%PCBUILD%amd64\\r
+    set BUILD=%Py_OutDir%amd64\\r
     set PGO=%~2\r
     set BUILD_PLAT=x64\r
     set OUTDIR_PLAT=amd64\r
@@ -170,7 +168,7 @@ if not "%SKIPBUILD%" EQU "1" (
 if "%OUTDIR_PLAT%" EQU "win32" (\r
     %MSBUILD% "%D%launcher\launcher.wixproj" /p:Platform=x86 %CERTOPTS% /p:ReleaseUri=%RELEASE_URI%\r
     if errorlevel 1 exit /B\r
-) else if not exist "%PCBUILD%win32\en-us\launcher.msi" (\r
+) else if not exist "%Py_OutDir%win32\en-us\launcher.msi" (\r
     %MSBUILD% "%D%launcher\launcher.wixproj" /p:Platform=x86 %CERTOPTS% /p:ReleaseUri=%RELEASE_URI%\r
     if errorlevel 1 exit /B\r
 )\r
index 24df0f5..16ef6ac 100644 (file)
                           Overwrite="true"
                           Lines="@(_LicenseFiles->'%(Content)')" />
     </Target>
-        
+    
+    <Target Name="_CopyMiscNews" AfterTargets="PrepareForBuild" Condition="Exists('$(PySourcePath)Misc\NEWS')">
+        <Copy SourceFiles="$(PySourcePath)Misc\NEWS" DestinationFiles="$(BuildPath)NEWS.txt" />
+    </Target>
+    
+    <Target Name="_MergeMiscNewsWithBlurb" AfterTargets="PrepareForBuild" Condition="$(Blurb) != '' and !Exists('$(PySourcePath)Misc\NEWS')">
+        <Exec Command="$(Blurb) merge -f &quot;$(BuildPath)NEWS.txt&quot;" WorkingDirectory="$(PCBuild)" />
+    </Target>
+    
+    <Target Name="_MergeMiscNewsWithPython" AfterTargets="PrepareForBuild" Condition="$(Blurb) == '' and !Exists('$(PySourcePath)Misc\NEWS')">
+        <ItemGroup>
+            <HostPython Include="$(ExternalsDir)python*\tools\python.exe" />
+            <HostPython Include="@(HostPython)" Condition="Exists(%(FullPath))" />
+            <HostPython Include="py" Condition="@(HostPython) == ''" />
+        </ItemGroup>
+        <PropertyGroup>
+            <HostPython>@(HostPython)</HostPython>
+            <HostPython Condition="$(HostPython.Contains(';'))">$(HostPython.Remove($(HostPython.IndexOf(';'))))</HostPython>
+        </PropertyGroup>
+        <Exec Command="&quot;$(HostPython)&quot; -m pip install -U blurb" WorkingDirectory="$(PCBuild)" />
+        <Exec Command="&quot;$(HostPython)&quot; -m blurb merge -f &quot;$(BuildPath)NEWS.txt&quot;" WorkingDirectory="$(PCBuild)" />
+    </Target>
+    
     <Import Project="..\msi.targets" />
 </Project>
\ No newline at end of file
index e675c21..394b4de 100644 (file)
@@ -6,7 +6,7 @@
                 <File Name="LICENSE.txt" Source="LICENSE" KeyPath="yes" />
             </Component>
             <Component Id="NEWS.txt" Directory="InstallDirectory" Guid="*">
-                <File Name="NEWS.txt" Source="!(bindpath.src)Misc\NEWS" KeyPath="yes" />
+                <File Name="NEWS.txt" KeyPath="yes" />
             </Component>
         </ComponentGroup>
     </Fragment>
index c7d13c6..81ad1f6 100644 (file)
@@ -12,7 +12,7 @@ set DO_FETCH=true
 set DO_CLEAN=false\r
 \r
 :CheckOpts\r
-if "%~1"=="--python" (set PYTHON_FOR_BUILD=%2) & shift & shift & goto CheckOpts\r
+if "%~1"=="--python" (set PYTHON=%2) & shift & shift & goto CheckOpts\r
 if "%~1"=="--organization" (set ORG=%2) & shift & shift & goto CheckOpts\r
 if "%~1"=="-c" (set DO_CLEAN=true) & shift & goto CheckOpts\r
 if "%~1"=="--clean" (set DO_CLEAN=true) & shift & goto CheckOpts\r
@@ -32,23 +32,7 @@ if "%DO_FETCH%"=="false" goto end
 \r
 if "%ORG%"=="" (set ORG=python)\r
 \r
-if "%PYTHON_FOR_BUILD%"=="" (\r
-    echo Checking for installed python...\r
-    py -3.6 -V >nul 2>&1 && (set PYTHON_FOR_BUILD=py -3.6)\r
-)\r
-if "%PYTHON_FOR_BUILD%"=="" (\r
-    if NOT exist "%EXTERNALS_DIR%" mkdir "%EXTERNALS_DIR%"\r
-    if NOT exist "%NUGET%" (\r
-        echo Downloading nuget...\r
-        rem NB: Must use single quotes around NUGET here, NOT double!\r
-        rem Otherwise, a space in the path would break things\r
-        powershell.exe -Command Invoke-WebRequest %NUGET_URL% -OutFile '%NUGET%'\r
-    )\r
-    echo Installing Python via nuget...\r
-    "%NUGET%" install pythonx86 -ExcludeVersion -OutputDirectory "%EXTERNALS_DIR%"\r
-    rem Quote it here; it's not quoted later because "py -3.6" wouldn't work\r
-    set PYTHON_FOR_BUILD="%EXTERNALS_DIR%\pythonx86\tools\python.exe"\r
-)\r
+call "%PCBUILD%\find_python.bat" "%PYTHON%"\r
 \r
 echo.Fetching external libraries...\r
 \r
@@ -59,7 +43,7 @@ for %%e in (%libraries%) do (
         echo.%%e already exists, skipping.\r
     ) else (\r
         echo.Fetching %%e...\r
-        %PYTHON_FOR_BUILD% "%PCBUILD%get_external.py" -e "%EXTERNALS_DIR%" -O %ORG% %%e\r
+        %PYTHON% "%PCBUILD%get_external.py" -e "%EXTERNALS_DIR%" -O %ORG% %%e\r
     )\r
 )\r
 \r
@@ -79,7 +63,7 @@ for %%b in (%binaries%) do (
         echo.%%b already exists, skipping.\r
     ) else (\r
         echo.Fetching %%b...\r
-        %PYTHON_FOR_BUILD% "%PCBUILD%get_external.py" -e "%EXTERNALS_DIR%" -b -O %ORG% %%b\r
+        %PYTHON% "%PCBUILD%get_external.py" -e "%EXTERNALS_DIR%" -b -O %ORG% %%b\r
     )\r
 )\r
 \r
index eb74565..e83d15c 100644 (file)
@@ -16,8 +16,9 @@
         <TargetPath>$(OutputPath)\$(TargetName)$(TargetExt)</TargetPath>\r
         <CleanCommand>rmdir /q/s "$(IntermediateOutputPath)\zip_$(ArchName)"</CleanCommand>\r
         <Arguments>"$(PythonExe)" "$(MSBuildThisFileDirectory)\make_zip.py"</Arguments>\r
-        <Arguments>$(Arguments) -e -o "$(TargetPath)" -t "$(IntermediateOutputPath)\zip_$(ArchName)" -a $(ArchName)</Arguments>\r
+        <Arguments>$(Arguments) -e -o "$(TargetPath)" -t "$(IntermediateOutputPath)\zip_$(ArchName)" -b "$(BuildPath.TrimEnd(`\`))"</Arguments>\r
         <Environment>set DOC_FILENAME=python$(PythonVersion).chm</Environment>\r
+        <Environment>$(Environment)%0D%0Aset PYTHONPATH=$(PySourcePath)Lib</Environment>\r
         <Environment Condition="Exists($(CRTRedist))">$(Environment)%0D%0Aset VCREDIST_PATH=$(CRTRedist)\$(Platform)</Environment>\r
     </PropertyGroup>\r
 \r
index 710e4a5..0698c14 100644 (file)
@@ -106,23 +106,23 @@ def include_in_tools(p):
 BASE_NAME = 'python{0.major}{0.minor}'.format(sys.version_info)
 
 FULL_LAYOUT = [
-    ('/', 'PCBuild/$arch', 'python.exe', is_not_debug),
-    ('/', 'PCBuild/$arch', 'pythonw.exe', is_not_debug),
-    ('/', 'PCBuild/$arch', 'python{}.dll'.format(sys.version_info.major), is_not_debug),
-    ('/', 'PCBuild/$arch', '{}.dll'.format(BASE_NAME), is_not_debug),
-    ('DLLs/', 'PCBuild/$arch', '*.pyd', is_not_debug),
-    ('DLLs/', 'PCBuild/$arch', '*.dll', is_not_debug_or_python),
+    ('/', '$build', 'python.exe', is_not_debug),
+    ('/', '$build', 'pythonw.exe', is_not_debug),
+    ('/', '$build', 'python{}.dll'.format(sys.version_info.major), is_not_debug),
+    ('/', '$build', '{}.dll'.format(BASE_NAME), is_not_debug),
+    ('DLLs/', '$build', '*.pyd', is_not_debug),
+    ('DLLs/', '$build', '*.dll', is_not_debug_or_python),
     ('include/', 'include', '*.h', None),
     ('include/', 'PC', 'pyconfig.h', None),
     ('Lib/', 'Lib', '**/*', include_in_lib),
-    ('libs/', 'PCBuild/$arch', '*.lib', include_in_libs),
+    ('libs/', '$build', '*.lib', include_in_libs),
     ('Tools/', 'Tools', '**/*', include_in_tools),
 ]
 
 EMBED_LAYOUT = [
-    ('/', 'PCBuild/$arch', 'python*.exe', is_not_debug),
-    ('/', 'PCBuild/$arch', '*.pyd', is_not_debug),
-    ('/', 'PCBuild/$arch', '*.dll', is_not_debug),
+    ('/', '$build', 'python*.exe', is_not_debug),
+    ('/', '$build', '*.pyd', is_not_debug),
+    ('/', '$build', '*.dll', is_not_debug),
     ('{}.zip'.format(BASE_NAME), 'Lib', '**/*', include_in_embeddable_lib),
 ]
 
@@ -187,15 +187,15 @@ def main():
     parser.add_argument('-o', '--out', metavar='file', help='The name of the output archive', type=Path, default=None)
     parser.add_argument('-t', '--temp', metavar='dir', help='A directory to temporarily extract files into', type=Path, default=None)
     parser.add_argument('-e', '--embed', help='Create an embedding layout', action='store_true', default=False)
-    parser.add_argument('-a', '--arch', help='Specify the architecture to use (win32/amd64)', type=str, default="win32")
+    parser.add_argument('-b', '--build', help='Specify the build directory', type=Path, default=None)
     ns = parser.parse_args()
 
     source = ns.source or (Path(__file__).resolve().parent.parent.parent)
     out = ns.out
-    arch = ns.arch
+    build = ns.build or Path(sys.exec_prefix)
     assert isinstance(source, Path)
     assert not out or isinstance(out, Path)
-    assert isinstance(arch, str)
+    assert isinstance(build, Path)
 
     if ns.temp:
         temp = ns.temp
@@ -218,7 +218,10 @@ def main():
 
     try:
         for t, s, p, c in layout:
-            fs = source / s.replace("$arch", arch)
+            if s == '$build':
+                fs = build
+            else:
+                fs = source / s
             files = rglob(fs, p, c)
             extra_files = []
             if s == 'Lib' and p == '**/*':
index 07b2573..80b0bd8 100644 (file)
@@ -22,9 +22,9 @@ if "%1" EQU "-t" (set TARGET=%~2) && shift && shift && goto CheckOpts
 if "%1" EQU "--target" (set TARGET=%~2) && shift && shift && goto CheckOpts\r
 if "%1" EQU "--dry-run" (set DRYRUN=true) && shift && goto CheckOpts\r
 if "%1" EQU "--skip-gpg" (set NOGPG=true) && shift && goto CheckOpts\r
-if "%1" EQU "--skip-purge" (set PURGE_OPTION=) && shift && godo CheckOpts\r
-if "%1" EQU "--skip-test" (set NOTEST=true) && shift && godo CheckOpts\r
-if "%1" EQU "-T" (set NOTEST=true) && shift && godo CheckOpts\r
+if "%1" EQU "--skip-purge" (set PURGE_OPTION=) && shift && goto CheckOpts\r
+if "%1" EQU "--skip-test" (set NOTEST=true) && shift && goto CheckOpts\r
+if "%1" EQU "-T" (set NOTEST=true) && shift && goto CheckOpts\r
 if "%1" NEQ "" echo Unexpected argument "%1" & exit /B 1\r
 \r
 if not defined PLINK where plink > "%TEMP%\plink.loc" 2> nul && set /P PLINK= < "%TEMP%\plink.loc" & del "%TEMP%\plink.loc"\r
index b02e770..5d303d2 100644 (file)
@@ -2,6 +2,7 @@
 setlocal\r
 set D=%~dp0\r
 set PCBUILD=%D%..\..\PCBuild\\r
+if "%Py_OutDir%"=="" set Py_OutDir=%PCBUILD%\r
 \r
 set BUILDX86=\r
 set BUILDX64=\r
@@ -28,7 +29,7 @@ if defined PACKAGES set PACKAGES="/p:Packages=%PACKAGES%"
 \r
 if defined BUILDX86 (\r
     if defined REBUILD ( call "%PCBUILD%build.bat" -e -r\r
-    ) else if not exist "%PCBUILD%win32\python.exe" call "%PCBUILD%build.bat" -e\r
+    ) else if not exist "%Py_OutDir%win32\python.exe" call "%PCBUILD%build.bat" -e\r
     if errorlevel 1 goto :eof\r
 \r
     %MSBUILD% "%D%make_pkg.proj" /p:Configuration=Release /p:Platform=x86 %OUTPUT% %PACKAGES%\r
@@ -37,7 +38,7 @@ if defined BUILDX86 (
 \r
 if defined BUILDX64 (\r
     if defined REBUILD ( call "%PCBUILD%build.bat" -p x64 -e -r\r
-    ) else if not exist "%PCBUILD%amd64\python.exe" call "%PCBUILD%build.bat" -p x64 -e\r
+    ) else if not exist "%Py_OutDir%amd64\python.exe" call "%PCBUILD%build.bat" -p x64 -e\r
     if errorlevel 1 goto :eof\r
 \r
     %MSBUILD% "%D%make_pkg.proj" /p:Configuration=Release /p:Platform=x64 %OUTPUT% %PACKAGES%\r
index 284f593..ba6c365 100644 (file)
@@ -4,6 +4,7 @@
         <ProjectGuid>{10487945-15D1-4092-A214-338395C4116B}</ProjectGuid>\r
         <OutputName>python</OutputName>\r
         <OutputName Condition="$(Platform) == 'x86'">$(OutputName)x86</OutputName>\r
+        <OutputName Condition="$(BuildForDaily) == 'true'">$(OutputName)daily</OutputName>\r
         <OutputSuffix></OutputSuffix>\r
         <SupportSigning>false</SupportSigning>\r
         <BuildForRelease Condition="$(BuildForRelease) == ''">true</BuildForRelease>\r
@@ -15,6 +16,7 @@
         <Nuget Condition="$(Nuget) == ''">$(ExternalsDir)\windows-installer\nuget\nuget.exe</Nuget>\r
         <NuspecVersion>$(MajorVersionNumber).$(MinorVersionNumber).$(MicroVersionNumber)</NuspecVersion>\r
         <NuspecVersion Condition="$(ReleaseLevelName) != ''">$(NuspecVersion)-$(ReleaseLevelName)</NuspecVersion>\r
+        <NuspecVersion Condition="$(BuildForDaily) == 'true'">$(MajorVersionNumber).$(MinorVersionNumber).$(DailyBuildVersion)</NuspecVersion>\r
         <SignOutput>false</SignOutput>\r
         <TargetName>$(OutputName).$(NuspecVersion)</TargetName>\r
         <TargetExt>.nupkg</TargetExt>\r
         <CleanCommand>rmdir /q/s "$(IntermediateOutputPath)"</CleanCommand>\r
         \r
         <PythonArguments>"$(PythonExe)" "$(MSBuildThisFileDirectory)\..\msi\make_zip.py"</PythonArguments>\r
-        <PythonArguments>$(PythonArguments) -t "$(IntermediateOutputPath)" -a $(ArchName)</PythonArguments>\r
+        <PythonArguments>$(PythonArguments) -t "$(IntermediateOutputPath)" -b "$(BuildPath.TrimEnd(`\`))"</PythonArguments>\r
         \r
         <PipArguments>"$(IntermediateOutputPath)\python.exe" -B -c "import sys; sys.path.append(r'$(PySourcePath)\Lib'); import ensurepip; ensurepip._main()"</PipArguments>\r
         <PackageArguments Condition="$(Packages) != ''">"$(IntermediateOutputPath)\python.exe" -B -m pip install -U $(Packages)</PackageArguments>\r
         \r
-        <NugetArguments>"$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).nuspec"</NugetArguments>\r
-        <NugetArguments>$(NugetArguments) -BasePath "$(IntermediateOutputPath)"</NugetArguments>\r
+        <NugetPackCommand>"$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).nuspec" -BasePath "$(IntermediateOutputPath)"</NugetPackCommand>\r
+        <NugetPackSymbolsCommand Condition="Exists('$(MSBuildThisFileDirectory)\$(OutputName).symbols.nuspec')">"$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).symbols.nuspec" -BasePath "$(BuildPath.TrimEnd(`\`))"</NugetPackSymbolsCommand>\r
         <NugetArguments>$(NugetArguments) -OutputDirectory "$(OutputPath.Trim(`\`))"</NugetArguments>\r
         <NugetArguments>$(NugetArguments) -Version "$(NuspecVersion)"</NugetArguments>\r
         <NugetArguments>$(NugetArguments) -NoPackageAnalysis -NonInteractive</NugetArguments>\r
         \r
         <Environment>set DOC_FILENAME=python$(PythonVersion).chm</Environment>\r
+        <Environment>$(Environment)%0D%0Aset PYTHONPATH=$(PySourcePath)Lib</Environment>\r
         <Environment Condition="Exists($(CRTRedist))">$(Environment)%0D%0Aset VCREDIST_PATH=$(CRTRedist)\$(Platform)</Environment>\r
+        <Environment>$(Environment)%0D%0Amkdir "$(OutputPath.Trim(`\`))" &gt;nul 2&gt;nul</Environment>\r
     </PropertyGroup>\r
 \r
     <Target Name="_NugetMissing" BeforeTargets="_Build" Condition="!Exists($(Nuget))">\r
         <Exec Command="setlocal%0D%0A$(Environment)%0D%0A$(PythonArguments)" />\r
         <Exec Command="$(PipArguments)" />\r
         <Exec Command="$(PackageArguments)" Condition="$(PackageArguments) != ''" />\r
-        <Exec Command="$(NugetArguments)" />\r
+\r
+        <PropertyGroup>\r
+            <_PropsContents>$([System.IO.File]::ReadAllText('python.props'))</_PropsContents>\r
+            <_PropsContents>$(_PropsContents.Replace('$$PYTHON_TAG$$', '$(MajorVersionNumber).$(MinorVersionNumber)'))</_PropsContents>\r
+            <_PropsContents>$(_PropsContents.Replace('$$PYTHON_VERSION$$', '$(NuspecVersion)'))</_PropsContents>\r
+            <_PropsContents Condition="$(Platform) == 'x86'">$(_PropsContents.Replace('$$PYTHON_PLATFORM$$', 'Win32'))</_PropsContents>\r
+            <_PropsContents Condition="$(Platform) != 'x86'">$(_PropsContents.Replace('$$PYTHON_PLATFORM$$', '$(Platform)'))</_PropsContents>\r
+            <_PropsContents>$(_PropsContents.Replace('$$PYTHON_TARGET$$', '_GetPythonRuntimeFilesDependsOn$(MajorVersionNumber)$(MinorVersionNumber)_$(Platform)'))</_PropsContents>\r
+            <_ExistingContents Condition="Exists('$(IntermediateOutputPath)\python.props')">$([System.IO.File]::ReadAllText('$(IntermediateOutputPath)\python.props'))</_ExistingContents>\r
+        </PropertyGroup>\r
+        <WriteLinesToFile File="$(IntermediateOutputPath)\python.props"\r
+                          Lines="$(_PropsContents)"\r
+                          Condition="$(_PropsContents) != $(_ExistingContents)" />\r
+\r
+        <Exec Command="$(NugetPackCommand) $(NugetArguments)" />\r
+        <Exec Command="$(NugetPackSymbolsCommand) $(NugetArguments)" Condition="$(NugetPackSymbolsCommand) != ''" />\r
     </Target>\r
 \r
     <Target Name="AfterBuild" />\r
index b3c5c34..d5f3e63 100644 (file)
@@ -13,6 +13,7 @@
     <tags>python</tags>
   </metadata>
   <files>
-    <file src="**\*" target="tools" />
+    <file src="**\*" exclude="python.props" target="tools" />
+    <file src="python.props" target="build\native" />
   </files>
 </package>
diff --git a/Tools/nuget/python.props b/Tools/nuget/python.props
new file mode 100644 (file)
index 0000000..b5ef67b
--- /dev/null
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+  <PropertyGroup Condition="$(Platform) == '$$PYTHON_PLATFORM$$'">\r
+    <PythonHome>$(MSBuildThisFileDirectory)\..\..\tools</PythonHome>\r
+    <PythonInclude>$(PythonHome)\include</PythonInclude>\r
+    <PythonLibs>$(PythonHome)\libs</PythonLibs>\r
+    <PythonTag>$$PYTHON_TAG$$</PythonTag>\r
+    <PythonVersion>$$PYTHON_VERSION$$</PythonVersion>\r
+    \r
+    <IncludePythonExe Condition="$(IncludePythonExe) == ''">true</IncludePythonExe>\r
+    <IncludeDistutils Condition="$(IncludeDistutils) == ''">false</IncludeDistutils>\r
+    <IncludeLib2To3 Condition="$(IncludeLib2To3) == ''">false</IncludeLib2To3>\r
+    <IncludeVEnv Condition="$(IncludeVEnv) == ''">false</IncludeVEnv>\r
+\r
+    <GetPythonRuntimeFilesDependsOn>$$PYTHON_TARGET$$;$(GetPythonRuntimeFilesDependsOn)</GetPythonRuntimeFilesDependsOn>\r
+  </PropertyGroup>\r
+\r
+  <ItemDefinitionGroup Condition="$(Platform) == '$$PYTHON_PLATFORM$$'">\r
+    <ClCompile>\r
+      <AdditionalIncludeDirectories>$(PythonInclude);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>\r
+    </ClCompile>\r
+    <Link>\r
+      <AdditionalLibraryDirectories>$(PythonLibs);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+\r
+  <Target Name="GetPythonRuntimeFiles" Returns="@(PythonRuntime)" DependsOnTargets="$(GetPythonRuntimeFilesDependsOn)" />\r
+\r
+  <Target Name="$$PYTHON_TARGET$$" Returns="@(PythonRuntime)">\r
+    <ItemGroup>\r
+      <_PythonRuntimeExe Include="$(PythonHome)\python*.dll" />\r
+      <_PythonRuntimeExe Include="$(PythonHome)\vcruntime140.dll" />\r
+      <_PythonRuntimeExe Include="$(PythonHome)\python*.exe" Condition="$(IncludePythonExe) == 'true'" />\r
+      <_PythonRuntimeExe>\r
+        <Link>%(Filename)%(Extension)</Link>\r
+      </_PythonRuntimeExe>\r
+      <_PythonRuntimeDlls Include="$(PythonHome)\DLLs\*.pyd" />\r
+      <_PythonRuntimeDlls Include="$(PythonHome)\DLLs\*.dll" />\r
+      <_PythonRuntimeDlls>\r
+        <Link>DLLs\%(Filename)%(Extension)</Link>\r
+      </_PythonRuntimeDlls>\r
+      <_PythonRuntimeLib Include="$(PythonHome)\Lib\**\*" Exclude="$(PythonHome)\Lib\**\*.pyc;$(PythonHome)\Lib\site-packages\**\*" />\r
+      <_PythonRuntimeLib Remove="$(PythonHome)\Lib\distutils\**\*" Condition="$(IncludeDistutils) != 'true'" />\r
+      <_PythonRuntimeLib Remove="$(PythonHome)\Lib\lib2to3\**\*" Condition="$(IncludeLib2To3) != 'true'" />\r
+      <_PythonRuntimeLib Remove="$(PythonHome)\Lib\ensurepip\**\*" Condition="$(IncludeVEnv) != 'true'" />\r
+      <_PythonRuntimeLib Remove="$(PythonHome)\Lib\venv\**\*" Condition="$(IncludeVEnv) != 'true'" />\r
+      <_PythonRuntimeLib>\r
+        <Link>Lib\%(RecursiveDir)%(Filename)%(Extension)</Link>\r
+      </_PythonRuntimeLib>\r
+      <PythonRuntime Include="@(_PythonRuntimeExe);@(_PythonRuntimeDlls);@(_PythonRuntimeLib)" />\r
+    </ItemGroup>\r
+    \r
+    <Message Importance="low" Text="Collected Python runtime from $(PythonHome):%0D%0A@(PythonRuntime->'  %(Link)','%0D%0A')" />\r
+  </Target>\r
+</Project>\r
diff --git a/Tools/nuget/pythondaily.nuspec b/Tools/nuget/pythondaily.nuspec
new file mode 100644 (file)
index 0000000..ee3343b
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<package >
+  <metadata>
+    <id>pythondaily</id>
+    <title>Python (Daily build)</title>
+    <version>0.0.0.0</version>
+    <authors>Python Software Foundation</authors>
+    <licenseUrl>https://docs.python.org/3/license.html</licenseUrl>
+    <projectUrl>https://www.python.org/</projectUrl>
+    <requireLicenseAcceptance>false</requireLicenseAcceptance>
+    <description>Installs an unsigned, untested build of Python for test purposes only.</description>
+    <iconUrl>https://www.python.org/static/favicon.ico</iconUrl>
+    <tags>python</tags>
+  </metadata>
+  <files>
+    <file src="**\*" exclude="python.props" target="tools" />
+    <file src="python.props" target="build\native" />
+  </files>
+</package>
index b55c879..ebfcd6c 100644 (file)
@@ -13,6 +13,7 @@
     <tags>python</tags>
   </metadata>
   <files>
-    <file src="**\*" target="tools" />
+    <file src="**\*" exclude="python.props" target="tools" />
+    <file src="python.props" target="build\native" />
   </files>
 </package>
index 33a9fea..1154b81 100755 (executable)
@@ -1,4 +1,5 @@
 #!/usr/bin/env python3
+"""Check proposed changes for common issues."""
 import re
 import sys
 import shutil
@@ -10,8 +11,17 @@ import reindent
 import untabify
 
 
+# Excluded directories which are copies of external libraries:
+# don't check their coding style
+EXCLUDE_DIRS = [os.path.join('Modules', '_ctypes', 'libffi'),
+                os.path.join('Modules', '_ctypes', 'libffi_osx'),
+                os.path.join('Modules', '_ctypes', 'libffi_msvc'),
+                os.path.join('Modules', '_decimal', 'libmpdec'),
+                os.path.join('Modules', 'expat'),
+                os.path.join('Modules', 'zlib')]
 SRCDIR = sysconfig.get_config_var('srcdir')
 
+
 def n_files_str(count):
     """Return 'N file(s)' with the proper plurality on 'file'."""
     return "{} file{}".format(count, "s" if count != 1 else "")
@@ -97,7 +107,7 @@ def changed_files(base_branch=None):
         if mq_patches_applied():
             cmd += ' --rev qparent'
         with subprocess.Popen(cmd.split(), stdout=subprocess.PIPE) as st:
-            return [x.decode().rstrip() for x in st.stdout]
+            filenames = [x.decode().rstrip() for x in st.stdout]
     elif os.path.exists(os.path.join(SRCDIR, '.git')):
         # We just use an existence check here as:
         #  directory = normal git checkout/clone
@@ -119,10 +129,20 @@ def changed_files(base_branch=None):
                     # file is renamed
                     filename = filename.split(' -> ', 2)[1].strip()
                 filenames.append(filename)
-        return filenames
     else:
         sys.exit('need a Mercurial or git checkout to get modified files')
 
+    filenames2 = []
+    for filename in filenames:
+        # Normalize the path to be able to match using .startswith()
+        filename = os.path.normpath(filename)
+        if any(filename.startswith(path) for path in EXCLUDE_DIRS):
+            # Exclude the file
+            continue
+        filenames2.append(filename)
+
+    return filenames2
+
 
 def report_modified_files(file_paths):
     count = len(file_paths)
@@ -135,7 +155,7 @@ def report_modified_files(file_paths):
         return "\n".join(lines)
 
 
-@status("Fixing whitespace", info=report_modified_files)
+@status("Fixing Python file whitespace", info=report_modified_files)
 def normalize_whitespace(file_paths):
     """Make sure that the whitespace for .py files have been normalized."""
     reindent.makebackup = False  # No need to create backups.
@@ -191,10 +211,11 @@ def credit_given(file_paths):
     return os.path.join('Misc', 'ACKS') in file_paths
 
 
-@status("Misc/NEWS updated", modal=True)
+@status("Misc/NEWS.d updated with `blurb`", modal=True)
 def reported_news(file_paths):
-    """Check if Misc/NEWS has been changed."""
-    return os.path.join('Misc', 'NEWS') in file_paths
+    """Check if Misc/NEWS.d has been changed."""
+    return any(p.startswith(os.path.join('Misc', 'NEWS.d', 'next'))
+               for p in file_paths)
 
 @status("configure regenerated", modal=True, info=str)
 def regenerated_configure(file_paths):
@@ -212,6 +233,27 @@ def regenerated_pyconfig_h_in(file_paths):
     else:
         return "not needed"
 
+def travis(pull_request):
+    if pull_request == 'false':
+        print('Not a pull request; skipping')
+        return
+    base_branch = get_base_branch()
+    file_paths = changed_files(base_branch)
+    python_files = [fn for fn in file_paths if fn.endswith('.py')]
+    c_files = [fn for fn in file_paths if fn.endswith(('.c', '.h'))]
+    doc_files = [fn for fn in file_paths if fn.startswith('Doc') and
+                 fn.endswith(('.rst', '.inc'))]
+    fixed = []
+    fixed.extend(normalize_whitespace(python_files))
+    fixed.extend(normalize_c_whitespace(c_files))
+    fixed.extend(normalize_docs_whitespace(doc_files))
+    if not fixed:
+        print('No whitespace issues found')
+    else:
+        print(f'Please fix the {len(fixed)} file(s) with whitespace issues')
+        print('(on UNIX you can run `make patchcheck` to make the fixes)')
+        sys.exit(1)
+
 def main():
     base_branch = get_base_branch()
     file_paths = changed_files(base_branch)
@@ -219,8 +261,7 @@ def main():
     c_files = [fn for fn in file_paths if fn.endswith(('.c', '.h'))]
     doc_files = [fn for fn in file_paths if fn.startswith('Doc') and
                  fn.endswith(('.rst', '.inc'))]
-    misc_files = {os.path.join('Misc', 'ACKS'), os.path.join('Misc', 'NEWS')}\
-            & set(file_paths)
+    misc_files = {p for p in file_paths if p.startswith('Misc')}
     # PEP 8 whitespace rules enforcement.
     normalize_whitespace(python_files)
     # C rules enforcement.
@@ -246,4 +287,12 @@ def main():
 
 
 if __name__ == '__main__':
-    main()
+    import argparse
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument('--travis',
+                        help='Perform pass/fail checks')
+    args = parser.parse_args()
+    if args.travis:
+        travis(args.travis)
+    else:
+        main()
diff --git a/Tools/ssl/multissltests.py b/Tools/ssl/multissltests.py
new file mode 100755 (executable)
index 0000000..994e420
--- /dev/null
@@ -0,0 +1,430 @@
+#!./python
+"""Run Python tests against multiple installations of OpenSSL and LibreSSL
+
+The script
+
+  (1) downloads OpenSSL / LibreSSL tar bundle
+  (2) extracts it to ./src
+  (3) compiles OpenSSL / LibreSSL
+  (4) installs OpenSSL / LibreSSL into ../multissl/$LIB/$VERSION/
+  (5) forces a recompilation of Python modules using the
+      header and library files from ../multissl/$LIB/$VERSION/
+  (6) runs Python's test suite
+
+The script must be run with Python's build directory as current working
+directory.
+
+The script uses LD_RUN_PATH, LD_LIBRARY_PATH, CPPFLAGS and LDFLAGS to bend
+search paths for header files and shared libraries. It's known to work on
+Linux with GCC and clang.
+
+Please keep this script compatible with Python 2.7, and 3.4 to 3.7.
+
+(c) 2013-2017 Christian Heimes <christian@python.org>
+"""
+from __future__ import print_function
+
+import argparse
+from datetime import datetime
+import logging
+import os
+try:
+    from urllib.request import urlopen
+except ImportError:
+    from urllib2 import urlopen
+import subprocess
+import shutil
+import sys
+import tarfile
+
+
+log = logging.getLogger("multissl")
+
+OPENSSL_OLD_VERSIONS = [
+     "0.9.8zc",
+     "0.9.8zh",
+     "1.0.1u",
+]
+
+OPENSSL_RECENT_VERSIONS = [
+     "1.0.2",
+     "1.0.2l",
+     "1.1.0f",
+]
+
+LIBRESSL_OLD_VERSIONS = [
+    "2.3.10",
+    "2.4.5",
+]
+
+LIBRESSL_RECENT_VERSIONS = [
+    "2.5.3",
+    "2.5.5",
+]
+
+# store files in ../multissl
+HERE = os.path.abspath(os.getcwd())
+MULTISSL_DIR = os.path.abspath(os.path.join(HERE, '..', 'multissl'))
+
+parser = argparse.ArgumentParser(
+    prog='multissl',
+    description=(
+        "Run CPython tests with multiple OpenSSL and LibreSSL "
+        "versions."
+    )
+)
+parser.add_argument(
+    '--debug',
+    action='store_true',
+    help="Enable debug mode",
+)
+parser.add_argument(
+    '--disable-ancient',
+    action='store_true',
+    help="Don't test OpenSSL < 1.0.2 and LibreSSL < 2.5.3.",
+)
+parser.add_argument(
+    '--openssl',
+    nargs='+',
+    default=(),
+    help=(
+        "OpenSSL versions, defaults to '{}' (ancient: '{}') if no "
+        "OpenSSL and LibreSSL versions are given."
+    ).format(OPENSSL_RECENT_VERSIONS, OPENSSL_OLD_VERSIONS)
+)
+parser.add_argument(
+    '--libressl',
+    nargs='+',
+    default=(),
+    help=(
+        "LibreSSL versions, defaults to '{}' (ancient: '{}') if no "
+        "OpenSSL and LibreSSL versions are given."
+    ).format(LIBRESSL_RECENT_VERSIONS, LIBRESSL_OLD_VERSIONS)
+)
+parser.add_argument(
+    '--tests',
+    nargs='*',
+    default=(),
+    help="Python tests to run, defaults to all SSL related tests.",
+)
+parser.add_argument(
+    '--base-directory',
+    default=MULTISSL_DIR,
+    help="Base directory for OpenSSL / LibreSSL sources and builds."
+)
+parser.add_argument(
+    '--no-network',
+    action='store_false',
+    dest='network',
+    help="Disable network tests."
+)
+parser.add_argument(
+    '--compile-only',
+    action='store_true',
+    help="Don't run tests, only compile _ssl.c and _hashopenssl.c."
+)
+
+
+class AbstractBuilder(object):
+    library = None
+    url_template = None
+    src_template = None
+    build_template = None
+
+    module_files = ("Modules/_ssl.c",
+                    "Modules/_hashopenssl.c")
+    module_libs = ("_ssl", "_hashlib")
+
+    def __init__(self, version, compile_args=(),
+                 basedir=MULTISSL_DIR):
+        self.version = version
+        self.compile_args = compile_args
+        # installation directory
+        self.install_dir = os.path.join(
+            os.path.join(basedir, self.library.lower()), version
+        )
+        # source file
+        self.src_dir = os.path.join(basedir, 'src')
+        self.src_file = os.path.join(
+            self.src_dir, self.src_template.format(version))
+        # build directory (removed after install)
+        self.build_dir = os.path.join(
+            self.src_dir, self.build_template.format(version))
+
+    def __str__(self):
+        return "<{0.__class__.__name__} for {0.version}>".format(self)
+
+    def __eq__(self, other):
+        if not isinstance(other, AbstractBuilder):
+            return NotImplemented
+        return (
+            self.library == other.library
+            and self.version == other.version
+        )
+
+    def __hash__(self):
+        return hash((self.library, self.version))
+
+    @property
+    def openssl_cli(self):
+        """openssl CLI binary"""
+        return os.path.join(self.install_dir, "bin", "openssl")
+
+    @property
+    def openssl_version(self):
+        """output of 'bin/openssl version'"""
+        cmd = [self.openssl_cli, "version"]
+        return self._subprocess_output(cmd)
+
+    @property
+    def pyssl_version(self):
+        """Value of ssl.OPENSSL_VERSION"""
+        cmd = [
+            sys.executable,
+            '-c', 'import ssl; print(ssl.OPENSSL_VERSION)'
+        ]
+        return self._subprocess_output(cmd)
+
+    @property
+    def include_dir(self):
+        return os.path.join(self.install_dir, "include")
+
+    @property
+    def lib_dir(self):
+        return os.path.join(self.install_dir, "lib")
+
+    @property
+    def has_openssl(self):
+        return os.path.isfile(self.openssl_cli)
+
+    @property
+    def has_src(self):
+        return os.path.isfile(self.src_file)
+
+    def _subprocess_call(self, cmd, env=None, **kwargs):
+        log.debug("Call '{}'".format(" ".join(cmd)))
+        return subprocess.check_call(cmd, env=env, **kwargs)
+
+    def _subprocess_output(self, cmd, env=None, **kwargs):
+        log.debug("Call '{}'".format(" ".join(cmd)))
+        if env is None:
+            env = os.environ.copy()
+            env["LD_LIBRARY_PATH"] = self.lib_dir
+        out = subprocess.check_output(cmd, env=env, **kwargs)
+        return out.strip().decode("utf-8")
+
+    def _download_src(self):
+        """Download sources"""
+        src_dir = os.path.dirname(self.src_file)
+        if not os.path.isdir(src_dir):
+            os.makedirs(src_dir)
+        url = self.url_template.format(self.version)
+        log.info("Downloading from {}".format(url))
+        req = urlopen(url)
+        # KISS, read all, write all
+        data = req.read()
+        log.info("Storing {}".format(self.src_file))
+        with open(self.src_file, "wb") as f:
+            f.write(data)
+
+    def _unpack_src(self):
+        """Unpack tar.gz bundle"""
+        # cleanup
+        if os.path.isdir(self.build_dir):
+            shutil.rmtree(self.build_dir)
+        os.makedirs(self.build_dir)
+
+        tf = tarfile.open(self.src_file)
+        name = self.build_template.format(self.version)
+        base = name + '/'
+        # force extraction into build dir
+        members = tf.getmembers()
+        for member in list(members):
+            if member.name == name:
+                members.remove(member)
+            elif not member.name.startswith(base):
+                raise ValueError(member.name, base)
+            member.name = member.name[len(base):].lstrip('/')
+        log.info("Unpacking files to {}".format(self.build_dir))
+        tf.extractall(self.build_dir, members)
+
+    def _build_src(self):
+        """Now build openssl"""
+        log.info("Running build in {}".format(self.build_dir))
+        cwd = self.build_dir
+        cmd = ["./config", "shared", "--prefix={}".format(self.install_dir)]
+        cmd.extend(self.compile_args)
+        self._subprocess_call(cmd, cwd=cwd)
+        # Old OpenSSL versions do not support parallel builds.
+        self._subprocess_call(["make", "-j1"], cwd=cwd)
+
+    def _make_install(self, remove=True):
+        self._subprocess_call(["make", "-j1", "install"], cwd=self.build_dir)
+        if remove:
+            shutil.rmtree(self.build_dir)
+
+    def install(self):
+        log.info(self.openssl_cli)
+        if not self.has_openssl:
+            if not self.has_src:
+                self._download_src()
+            else:
+                log.debug("Already has src {}".format(self.src_file))
+            self._unpack_src()
+            self._build_src()
+            self._make_install()
+        else:
+            log.info("Already has installation {}".format(self.install_dir))
+        # validate installation
+        version = self.openssl_version
+        if self.version not in version:
+            raise ValueError(version)
+
+    def recompile_pymods(self):
+        log.warning("Using build from {}".format(self.build_dir))
+        # force a rebuild of all modules that use OpenSSL APIs
+        for fname in self.module_files:
+            os.utime(fname, None)
+        # remove all build artefacts
+        for root, dirs, files in os.walk('build'):
+            for filename in files:
+                if filename.startswith(self.module_libs):
+                    os.unlink(os.path.join(root, filename))
+
+        # overwrite header and library search paths
+        env = os.environ.copy()
+        env["CPPFLAGS"] = "-I{}".format(self.include_dir)
+        env["LDFLAGS"] = "-L{}".format(self.lib_dir)
+        # set rpath
+        env["LD_RUN_PATH"] = self.lib_dir
+
+        log.info("Rebuilding Python modules")
+        cmd = [sys.executable, "setup.py", "build"]
+        self._subprocess_call(cmd, env=env)
+        self.check_imports()
+
+    def check_imports(self):
+        cmd = [sys.executable, "-c", "import _ssl; import _hashlib"]
+        self._subprocess_call(cmd)
+
+    def check_pyssl(self):
+        version = self.pyssl_version
+        if self.version not in version:
+            raise ValueError(version)
+
+    def run_python_tests(self, tests, network=True):
+        if not tests:
+            cmd = [sys.executable, 'Lib/test/ssltests.py', '-j0']
+        elif sys.version_info < (3, 3):
+            cmd = [sys.executable, '-m', 'test.regrtest']
+        else:
+            cmd = [sys.executable, '-m', 'test', '-j0']
+        if network:
+            cmd.extend(['-u', 'network', '-u', 'urlfetch'])
+        cmd.extend(['-w', '-r'])
+        cmd.extend(tests)
+        self._subprocess_call(cmd, stdout=None)
+
+
+class BuildOpenSSL(AbstractBuilder):
+    library = "OpenSSL"
+    url_template = "https://www.openssl.org/source/openssl-{}.tar.gz"
+    src_template = "openssl-{}.tar.gz"
+    build_template = "openssl-{}"
+
+
+class BuildLibreSSL(AbstractBuilder):
+    library = "LibreSSL"
+    url_template = (
+        "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-{}.tar.gz")
+    src_template = "libressl-{}.tar.gz"
+    build_template = "libressl-{}"
+
+
+def configure_make():
+    if not os.path.isfile('Makefile'):
+        log.info('Running ./configure')
+        subprocess.check_call([
+            './configure', '--config-cache', '--quiet',
+            '--with-pydebug'
+        ])
+
+    log.info('Running make')
+    subprocess.check_call(['make', '--quiet'])
+
+
+def main():
+    args = parser.parse_args()
+    if not args.openssl and not args.libressl:
+        args.openssl = list(OPENSSL_RECENT_VERSIONS)
+        args.libressl = list(LIBRESSL_RECENT_VERSIONS)
+        if not args.disable_ancient:
+            args.openssl.extend(OPENSSL_OLD_VERSIONS)
+            args.libressl.extend(LIBRESSL_OLD_VERSIONS)
+
+    logging.basicConfig(
+        level=logging.DEBUG if args.debug else logging.INFO,
+        format="*** %(levelname)s %(message)s"
+    )
+
+    start = datetime.now()
+
+    for name in ['python', 'setup.py', 'Modules/_ssl.c']:
+        if not os.path.isfile(name):
+            parser.error(
+                "Must be executed from CPython build dir"
+            )
+    if not os.path.samefile('python', sys.executable):
+        parser.error(
+            "Must be executed with ./python from CPython build dir"
+        )
+
+    # check for configure and run make
+    configure_make()
+
+    # download and register builder
+    builds = []
+
+    for version in args.openssl:
+        build = BuildOpenSSL(version)
+        build.install()
+        builds.append(build)
+
+    for version in args.libressl:
+        build = BuildLibreSSL(version)
+        build.install()
+        builds.append(build)
+
+    for build in builds:
+        try:
+            build.recompile_pymods()
+            build.check_pyssl()
+            if not args.compile_only:
+                build.run_python_tests(
+                    tests=args.tests,
+                    network=args.network,
+                )
+        except Exception as e:
+            log.exception("%s failed", build)
+            print("{} failed: {}".format(build, e), file=sys.stderr)
+            sys.exit(2)
+
+    print("\n{} finished in {}".format(
+        "Tests" if not args.compile_only else "Builds",
+        datetime.now() - start
+    ))
+    print('Python: ', sys.version)
+    if args.compile_only:
+        print('Build only')
+    elif args.tests:
+        print('Executed Tests:', ' '.join(args.tests))
+    else:
+        print('Executed all SSL tests.')
+
+    print('OpenSSL / LibreSSL versions:')
+    for build in builds:
+        print("    * {0.library} {0.version}".format(build))
+
+
+if __name__ == "__main__":
+    main()
diff --git a/Tools/ssl/test_multiple_versions.py b/Tools/ssl/test_multiple_versions.py
deleted file mode 100644 (file)
index 30d5fcf..0000000
+++ /dev/null
@@ -1,241 +0,0 @@
-#./python
-"""Run Python tests with multiple installations of OpenSSL
-
-The script
-
-  (1) downloads OpenSSL tar bundle
-  (2) extracts it to ../openssl/src/openssl-VERSION/
-  (3) compiles OpenSSL
-  (4) installs OpenSSL into ../openssl/VERSION/
-  (5) forces a recompilation of Python modules using the
-      header and library files from ../openssl/VERSION/
-  (6) runs Python's test suite
-
-The script must be run with Python's build directory as current working
-directory:
-
-    ./python Tools/ssl/test_multiple_versions.py
-
-The script uses LD_RUN_PATH, LD_LIBRARY_PATH, CPPFLAGS and LDFLAGS to bend
-search paths for header files and shared libraries. It's known to work on
-Linux with GCC 4.x.
-
-(c) 2013 Christian Heimes <christian@python.org>
-"""
-import logging
-import os
-import tarfile
-import shutil
-import subprocess
-import sys
-from urllib.request import urlopen
-
-log = logging.getLogger("multissl")
-
-OPENSSL_VERSIONS = [
-    "0.9.7m", "0.9.8i", "0.9.8l", "0.9.8m", "0.9.8y", "1.0.0k", "1.0.1e"
-]
-FULL_TESTS = [
-    "test_asyncio", "test_ftplib", "test_hashlib", "test_httplib",
-    "test_imaplib", "test_nntplib", "test_poplib", "test_smtplib",
-    "test_smtpnet", "test_urllib2_localnet", "test_venv"
-]
-MINIMAL_TESTS = ["test_ssl", "test_hashlib"]
-CADEFAULT = True
-HERE = os.path.abspath(os.getcwd())
-DEST_DIR = os.path.abspath(os.path.join(HERE, os.pardir, "openssl"))
-
-
-class BuildSSL:
-    url_template = "https://www.openssl.org/source/openssl-{}.tar.gz"
-
-    module_files = ["Modules/_ssl.c",
-                    "Modules/socketmodule.c",
-                    "Modules/_hashopenssl.c"]
-
-    def __init__(self, version, openssl_compile_args=(), destdir=DEST_DIR):
-        self._check_python_builddir()
-        self.version = version
-        self.openssl_compile_args = openssl_compile_args
-        # installation directory
-        self.install_dir = os.path.join(destdir, version)
-        # source file
-        self.src_file = os.path.join(destdir, "src",
-                                     "openssl-{}.tar.gz".format(version))
-        # build directory (removed after install)
-        self.build_dir = os.path.join(destdir, "src",
-                                      "openssl-{}".format(version))
-
-    @property
-    def openssl_cli(self):
-        """openssl CLI binary"""
-        return os.path.join(self.install_dir, "bin", "openssl")
-
-    @property
-    def openssl_version(self):
-        """output of 'bin/openssl version'"""
-        env = os.environ.copy()
-        env["LD_LIBRARY_PATH"] = self.lib_dir
-        cmd = [self.openssl_cli, "version"]
-        return self._subprocess_output(cmd, env=env)
-
-    @property
-    def pyssl_version(self):
-        """Value of ssl.OPENSSL_VERSION"""
-        env = os.environ.copy()
-        env["LD_LIBRARY_PATH"] = self.lib_dir
-        cmd = ["./python", "-c", "import ssl; print(ssl.OPENSSL_VERSION)"]
-        return self._subprocess_output(cmd, env=env)
-
-    @property
-    def include_dir(self):
-        return os.path.join(self.install_dir, "include")
-
-    @property
-    def lib_dir(self):
-        return os.path.join(self.install_dir, "lib")
-
-    @property
-    def has_openssl(self):
-        return os.path.isfile(self.openssl_cli)
-
-    @property
-    def has_src(self):
-        return os.path.isfile(self.src_file)
-
-    def _subprocess_call(self, cmd, stdout=subprocess.DEVNULL, env=None,
-                         **kwargs):
-        log.debug("Call '%s'", " ".join(cmd))
-        return subprocess.check_call(cmd, stdout=stdout, env=env, **kwargs)
-
-    def _subprocess_output(self, cmd, env=None, **kwargs):
-        log.debug("Call '%s'", " ".join(cmd))
-        out = subprocess.check_output(cmd, env=env)
-        return out.strip().decode("utf-8")
-
-    def _check_python_builddir(self):
-        if not os.path.isfile("python") or not os.path.isfile("setup.py"):
-            raise ValueError("Script must be run in Python build directory")
-
-    def _download_openssl(self):
-        """Download OpenSSL source dist"""
-        src_dir = os.path.dirname(self.src_file)
-        if not os.path.isdir(src_dir):
-            os.makedirs(src_dir)
-        url = self.url_template.format(self.version)
-        log.info("Downloading OpenSSL from {}".format(url))
-        req = urlopen(url, cadefault=CADEFAULT)
-        # KISS, read all, write all
-        data = req.read()
-        log.info("Storing {}".format(self.src_file))
-        with open(self.src_file, "wb") as f:
-            f.write(data)
-
-    def _unpack_openssl(self):
-        """Unpack tar.gz bundle"""
-        # cleanup
-        if os.path.isdir(self.build_dir):
-            shutil.rmtree(self.build_dir)
-        os.makedirs(self.build_dir)
-
-        tf = tarfile.open(self.src_file)
-        base = "openssl-{}/".format(self.version)
-        # force extraction into build dir
-        members = tf.getmembers()
-        for member in members:
-            if not member.name.startswith(base):
-                raise ValueError(member.name)
-            member.name = member.name[len(base):]
-        log.info("Unpacking files to {}".format(self.build_dir))
-        tf.extractall(self.build_dir, members)
-
-    def _build_openssl(self):
-        """Now build openssl"""
-        log.info("Running build in {}".format(self.install_dir))
-        cwd = self.build_dir
-        cmd = ["./config", "shared", "--prefix={}".format(self.install_dir)]
-        cmd.extend(self.openssl_compile_args)
-        self._subprocess_call(cmd, cwd=cwd)
-        self._subprocess_call(["make"], cwd=cwd)
-
-    def _install_openssl(self, remove=True):
-        self._subprocess_call(["make", "install"], cwd=self.build_dir)
-        if remove:
-            shutil.rmtree(self.build_dir)
-
-    def install_openssl(self):
-        if not self.has_openssl:
-            if not self.has_src:
-                self._download_openssl()
-            else:
-                log.debug("Already has src %s", self.src_file)
-            self._unpack_openssl()
-            self._build_openssl()
-            self._install_openssl()
-        else:
-            log.info("Already has installation {}".format(self.install_dir))
-        # validate installation
-        version = self.openssl_version
-        if self.version not in version:
-            raise ValueError(version)
-
-    def touch_pymods(self):
-        # force a rebuild of all modules that use OpenSSL APIs
-        for fname in self.module_files:
-            os.utime(fname)
-
-    def recompile_pymods(self):
-        log.info("Using OpenSSL build from {}".format(self.build_dir))
-        # overwrite header and library search paths
-        env = os.environ.copy()
-        env["CPPFLAGS"] = "-I{}".format(self.include_dir)
-        env["LDFLAGS"] = "-L{}".format(self.lib_dir)
-        # set rpath
-        env["LD_RUN_PATH"] = self.lib_dir
-
-        log.info("Rebuilding Python modules")
-        self.touch_pymods()
-        cmd = ["./python", "setup.py", "build"]
-        self._subprocess_call(cmd, env=env)
-
-    def check_pyssl(self):
-        version = self.pyssl_version
-        if self.version not in version:
-            raise ValueError(version)
-
-    def run_pytests(self, *args):
-        cmd = ["./python", "-m", "test"]
-        cmd.extend(args)
-        self._subprocess_call(cmd, stdout=None)
-
-    def run_python_tests(self, *args):
-        self.recompile_pymods()
-        self.check_pyssl()
-        self.run_pytests(*args)
-
-
-def main(*args):
-    builders = []
-    for version in OPENSSL_VERSIONS:
-        if version in ("0.9.8i", "0.9.8l"):
-            openssl_compile_args = ("no-asm",)
-        else:
-            openssl_compile_args = ()
-        builder = BuildSSL(version, openssl_compile_args)
-        builder.install_openssl()
-        builders.append(builder)
-
-    for builder in builders:
-        builder.run_python_tests(*args)
-    # final touch
-    builder.touch_pymods()
-
-
-if __name__ == "__main__":
-    logging.basicConfig(level=logging.INFO,
-                        format="*** %(levelname)s %(message)s")
-    args = sys.argv[1:]
-    if not args:
-        args = ["-unetwork", "-v"]
-        args.extend(FULL_TESTS)
-    main(*args)
index 2a745e5..5fadcb1 100644 (file)
@@ -12,9 +12,9 @@
 # PARTICULAR PURPOSE.
 
 m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
-dnl pkg.m4 - Macros to locate and utilise pkg-config.   -*- Autoconf -*-
-dnl serial 11 (pkg-config-0.29.1)
-dnl
+# pkg.m4 - Macros to locate and utilise pkg-config.   -*- Autoconf -*-
+# serial 11 (pkg-config-0.29.1)
+
 dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
 dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com>
 dnl
@@ -288,3 +288,71 @@ AS_VAR_COPY([$1], [pkg_cv_][$1])
 AS_VAR_IF([$1], [""], [$5], [$4])dnl
 ])dnl PKG_CHECK_VAR
 
+dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES,
+dnl   [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND],
+dnl   [DESCRIPTION], [DEFAULT])
+dnl ------------------------------------------
+dnl
+dnl Prepare a "--with-" configure option using the lowercase
+dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and
+dnl PKG_CHECK_MODULES in a single macro.
+AC_DEFUN([PKG_WITH_MODULES],
+[
+m4_pushdef([with_arg], m4_tolower([$1]))
+
+m4_pushdef([description],
+           [m4_default([$5], [build with ]with_arg[ support])])
+
+m4_pushdef([def_arg], [m4_default([$6], [auto])])
+m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes])
+m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no])
+
+m4_case(def_arg,
+            [yes],[m4_pushdef([with_without], [--without-]with_arg)],
+            [m4_pushdef([with_without],[--with-]with_arg)])
+
+AC_ARG_WITH(with_arg,
+     AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),,
+    [AS_TR_SH([with_]with_arg)=def_arg])
+
+AS_CASE([$AS_TR_SH([with_]with_arg)],
+            [yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)],
+            [auto],[PKG_CHECK_MODULES([$1],[$2],
+                                        [m4_n([def_action_if_found]) $3],
+                                        [m4_n([def_action_if_not_found]) $4])])
+
+m4_popdef([with_arg])
+m4_popdef([description])
+m4_popdef([def_arg])
+
+])dnl PKG_WITH_MODULES
+
+dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES,
+dnl   [DESCRIPTION], [DEFAULT])
+dnl -----------------------------------------------
+dnl
+dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES
+dnl check._[VARIABLE-PREFIX] is exported as make variable.
+AC_DEFUN([PKG_HAVE_WITH_MODULES],
+[
+PKG_WITH_MODULES([$1],[$2],,,[$3],[$4])
+
+AM_CONDITIONAL([HAVE_][$1],
+               [test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"])
+])dnl PKG_HAVE_WITH_MODULES
+
+dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES,
+dnl   [DESCRIPTION], [DEFAULT])
+dnl ------------------------------------------------------
+dnl
+dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after
+dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make
+dnl and preprocessor variable.
+AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES],
+[
+PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4])
+
+AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"],
+        [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])])
+])dnl PKG_HAVE_DEFINE_WITH_MODULES
+
index 10ac675..ed305a8 100755 (executable)
--- a/configure
+++ b/configure
@@ -7822,7 +7822,7 @@ sys/stat.h sys/syscall.h sys/sys_domain.h sys/termio.h sys/time.h \
 sys/times.h sys/types.h sys/uio.h sys/un.h sys/utsname.h sys/wait.h pty.h \
 libutil.h sys/resource.h netpacket/packet.h sysexits.h bluetooth.h \
 linux/tipc.h linux/random.h spawn.h util.h alloca.h endian.h \
-sys/endian.h
+sys/endian.h sys/sysmacros.h
 do :
   as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
 ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
index 9620067..e400fa1 100644 (file)
@@ -2068,7 +2068,7 @@ sys/stat.h sys/syscall.h sys/sys_domain.h sys/termio.h sys/time.h \
 sys/times.h sys/types.h sys/uio.h sys/un.h sys/utsname.h sys/wait.h pty.h \
 libutil.h sys/resource.h netpacket/packet.h sysexits.h bluetooth.h \
 linux/tipc.h linux/random.h spawn.h util.h alloca.h endian.h \
-sys/endian.h)
+sys/endian.h sys/sysmacros.h)
 AC_HEADER_DIRENT
 AC_HEADER_MAJOR
 
index b10c57f..cdcb570 100644 (file)
 /* Define to 1 if you have the <sys/syscall.h> header file. */
 #undef HAVE_SYS_SYSCALL_H
 
+/* Define to 1 if you have the <sys/sysmacros.h> header file. */
+#undef HAVE_SYS_SYSMACROS_H
+
 /* Define to 1 if you have the <sys/sys_domain.h> header file. */
 #undef HAVE_SYS_SYS_DOMAIN_H
 
index fe47797..1cd1503 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -1503,6 +1503,9 @@ class PyBuildExt(build_ext):
             expat_inc = [os.path.join(os.getcwd(), srcdir, 'Modules', 'expat')]
             define_macros = [
                 ('HAVE_EXPAT_CONFIG_H', '1'),
+                # bpo-30947: Python uses best available entropy sources to
+                # call XML_SetHashSalt(), expat entropy sources are not needed
+                ('XML_POOR_ENTROPY', '1'),
             ]
             expat_lib = []
             expat_sources = ['expat/xmlparse.c',