Imported Upstream version 3.7.2
authorHyunjee Kim <hj0426.kim@samsung.com>
Wed, 20 Mar 2019 03:05:24 +0000 (12:05 +0900)
committerHyunjee Kim <hj0426.kim@samsung.com>
Wed, 20 Mar 2019 03:05:24 +0000 (12:05 +0900)
502 files changed:
.azure-pipelines/docs-steps.yml
.azure-pipelines/windows-appx-test.yml [new file with mode: 0644]
.azure-pipelines/windows-steps.yml
Doc/c-api/buffer.rst
Doc/c-api/conversion.rst
Doc/c-api/exceptions.rst
Doc/c-api/gcsupport.rst
Doc/c-api/init.rst
Doc/c-api/memory.rst
Doc/c-api/structures.rst
Doc/c-api/type.rst
Doc/c-api/veryhigh.rst
Doc/conf.py
Doc/distutils/apiref.rst
Doc/distutils/builtdist.rst
Doc/distutils/configfile.rst
Doc/distutils/setupscript.rst
Doc/docutils.conf [deleted file]
Doc/extending/extending.rst
Doc/faq/design.rst
Doc/faq/extending.rst
Doc/faq/windows.rst
Doc/glossary.rst
Doc/howto/logging.rst
Doc/install/index.rst
Doc/installing/index.rst
Doc/library/_thread.rst
Doc/library/abc.rst
Doc/library/argparse.rst
Doc/library/ast.rst
Doc/library/asyncio-dev.rst
Doc/library/asyncio-eventloop.rst
Doc/library/asyncio-policy.rst
Doc/library/asyncio-protocol.rst
Doc/library/asyncio-stream.rst
Doc/library/asyncio-task.rst
Doc/library/asyncore.rst
Doc/library/cmd.rst
Doc/library/codecs.rst
Doc/library/collections.rst
Doc/library/compileall.rst
Doc/library/concurrent.futures.rst
Doc/library/configparser.rst
Doc/library/constants.rst
Doc/library/copy.rst
Doc/library/crypt.rst
Doc/library/curses.ascii.rst
Doc/library/datetime.rst
Doc/library/difflib.rst
Doc/library/doctest.rst
Doc/library/email.compat32-message.rst
Doc/library/email.message.rst
Doc/library/email.parser.rst
Doc/library/email.rst
Doc/library/email.utils.rst [moved from Doc/library/email.util.rst with 100% similarity]
Doc/library/exceptions.rst
Doc/library/fnmatch.rst
Doc/library/functions.rst
Doc/library/functools.rst
Doc/library/gettext.rst
Doc/library/glob.rst
Doc/library/hashlib.rst
Doc/library/heapq.rst
Doc/library/http.server.rst
Doc/library/idle.rst
Doc/library/imp.rst
Doc/library/importlib.rst
Doc/library/inspect.rst
Doc/library/intro.rst
Doc/library/io.rst
Doc/library/json.rst
Doc/library/locale.rst
Doc/library/logging.rst
Doc/library/mimetypes.rst
Doc/library/mmap.rst
Doc/library/optparse.rst
Doc/library/os.path.rst
Doc/library/os.rst
Doc/library/ossaudiodev.rst
Doc/library/pickle.rst
Doc/library/pprint.rst
Doc/library/py_compile.rst
Doc/library/queue.rst
Doc/library/re.rst
Doc/library/reprlib.rst
Doc/library/resource.rst
Doc/library/select.rst
Doc/library/shutil.rst
Doc/library/signal.rst
Doc/library/site.rst
Doc/library/smtplib.rst
Doc/library/socket.rst
Doc/library/sqlite3.rst
Doc/library/ssl.rst
Doc/library/stat.rst
Doc/library/stdtypes.rst
Doc/library/string.rst
Doc/library/struct.rst
Doc/library/subprocess.rst
Doc/library/sys.rst
Doc/library/sysconfig.rst
Doc/library/termios.rst
Doc/library/textwrap.rst
Doc/library/threading.rst
Doc/library/time.rst
Doc/library/tkinter.rst
Doc/library/traceback.rst
Doc/library/turtle.rst
Doc/library/typing.rst
Doc/library/unittest.mock-examples.rst
Doc/library/unittest.mock.rst
Doc/library/unittest.rst
Doc/library/urllib.parse.rst
Doc/library/urllib.request.rst
Doc/library/venv.rst
Doc/library/winreg.rst
Doc/library/xml.dom.minidom.rst
Doc/library/xml.rst
Doc/library/xml.sax.rst
Doc/library/zipfile.rst
Doc/make.bat
Doc/reference/compound_stmts.rst
Doc/reference/datamodel.rst
Doc/reference/executionmodel.rst
Doc/reference/expressions.rst
Doc/reference/import.rst
Doc/reference/lexical_analysis.rst
Doc/reference/simple_stmts.rst
Doc/tools/extensions/escape4chm.py
Doc/tools/extensions/pyspecific.py
Doc/tools/rstlint.py
Doc/tools/static/switchers.js
Doc/tools/susp-ignored.csv
Doc/tools/templates/indexsidebar.html
Doc/tutorial/classes.rst
Doc/tutorial/controlflow.rst
Doc/tutorial/datastructures.rst
Doc/tutorial/errors.rst
Doc/tutorial/introduction.rst
Doc/using/cmdline.rst
Doc/using/unix.rst
Doc/using/venv-create.inc
Doc/using/windows.rst
Doc/whatsnew/2.2.rst
Doc/whatsnew/2.3.rst
Doc/whatsnew/2.5.rst
Doc/whatsnew/3.4.rst
Doc/whatsnew/3.5.rst
Doc/whatsnew/3.6.rst
Doc/whatsnew/3.7.rst
Include/Python.h
Include/ceval.h
Include/fileutils.h
Include/object.h
Include/patchlevel.h
Include/pyerrors.h
Include/pylifecycle.h
Include/pymem.h
Include/unicodeobject.h
Lib/_osx_support.py
Lib/_pydecimal.py
Lib/asyncio/base_events.py
Lib/asyncio/proactor_events.py
Lib/asyncio/selector_events.py
Lib/cProfile.py
Lib/cgi.py
Lib/compileall.py
Lib/concurrent/futures/thread.py
Lib/ctypes/test/test_bytes.py
Lib/dataclasses.py
Lib/datetime.py
Lib/distutils/_msvccompiler.py
Lib/distutils/archive_util.py
Lib/distutils/command/bdist_dumb.py
Lib/distutils/command/bdist_msi.py
Lib/distutils/command/bdist_rpm.py
Lib/distutils/command/bdist_wininst.py
Lib/distutils/command/build_ext.py
Lib/distutils/tests/test_archive_util.py
Lib/distutils/tests/test_bdist_dumb.py
Lib/distutils/tests/test_sdist.py
Lib/email/_header_value_parser.py
Lib/ensurepip/__init__.py
Lib/ensurepip/_bundled/pip-10.0.1-py2.py3-none-any.whl [deleted file]
Lib/ensurepip/_bundled/pip-18.1-py2.py3-none-any.whl [new file with mode: 0644]
Lib/ensurepip/_bundled/setuptools-40.6.2-py2.py3-none-any.whl [moved from Lib/ensurepip/_bundled/setuptools-39.0.1-py2.py3-none-any.whl with 54% similarity]
Lib/enum.py
Lib/ftplib.py
Lib/idlelib/NEWS.txt
Lib/idlelib/codecontext.py
Lib/idlelib/config.py
Lib/idlelib/debugger_r.py
Lib/idlelib/filelist.py
Lib/idlelib/help.html
Lib/idlelib/help.py
Lib/idlelib/hyperparser.py
Lib/idlelib/idle_test/htest.py
Lib/idlelib/idle_test/mock_tk.py
Lib/idlelib/idle_test/test_browser.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_rpc.py
Lib/idlelib/idle_test/test_zoomheight.py
Lib/idlelib/iomenu.py
Lib/idlelib/macosx.py
Lib/idlelib/outwin.py
Lib/idlelib/pyparse.py
Lib/idlelib/pyshell.py
Lib/imaplib.py
Lib/importlib/abc.py
Lib/inspect.py
Lib/lib2to3/fixes/fix_execfile.py
Lib/lib2to3/tests/test_fixers.py
Lib/logging/__init__.py
Lib/mimetypes.py
Lib/multiprocessing/pool.py
Lib/ntpath.py
Lib/platform.py
Lib/poplib.py
Lib/posixpath.py
Lib/py_compile.py
Lib/pydoc.py
Lib/pydoc_data/topics.py
Lib/shutil.py
Lib/smtplib.py
Lib/socketserver.py
Lib/sqlite3/test/regression.py
Lib/ssl.py
Lib/subprocess.py
Lib/test/_test_multiprocessing.py
Lib/test/datetimetester.py
Lib/test/eintrdata/eintr_tester.py
Lib/test/libregrtest/cmdline.py
Lib/test/libregrtest/main.py
Lib/test/libregrtest/runtest.py
Lib/test/libregrtest/runtest_mp.py
Lib/test/pythoninfo.py
Lib/test/signalinterproctester.py
Lib/test/support/__init__.py
Lib/test/support/testresult.py
Lib/test/test__osx_support.py
Lib/test/test_asyncio/keycert3.pem [deleted file]
Lib/test/test_asyncio/pycacert.pem [deleted file]
Lib/test/test_asyncio/ssl_cert.pem [deleted file]
Lib/test/test_asyncio/ssl_key.pem [deleted file]
Lib/test/test_asyncio/test_base_events.py
Lib/test/test_asyncio/test_selector_events.py
Lib/test/test_asyncio/test_sslproto.py
Lib/test/test_asyncio/utils.py
Lib/test/test_bdb.py
Lib/test/test_builtin.py
Lib/test/test_bytes.py
Lib/test/test_c_locale_coercion.py
Lib/test/test_capi.py
Lib/test/test_cgi.py
Lib/test/test_cmd_line_script.py
Lib/test/test_codecs.py
Lib/test/test_compile.py
Lib/test/test_compileall.py
Lib/test/test_cprofile.py
Lib/test/test_dataclasses.py
Lib/test/test_decimal.py
Lib/test/test_eintr.py
Lib/test/test_email/test_utils.py
Lib/test/test_embed.py
Lib/test/test_enum.py
Lib/test/test_gdb.py
Lib/test/test_http_cookiejar.py
Lib/test/test_importlib/source/test_file_loader.py
Lib/test/test_io.py
Lib/test/test_largefile.py
Lib/test/test_minidom.py
Lib/test/test_multiprocessing_fork.py
Lib/test/test_multiprocessing_forkserver.py
Lib/test/test_ntpath.py
Lib/test/test_ordered_dict.py
Lib/test/test_os.py
Lib/test/test_pathlib.py
Lib/test/test_posixpath.py
Lib/test/test_py_compile.py
Lib/test/test_pydoc.py
Lib/test/test_range.py
Lib/test/test_regrtest.py
Lib/test/test_site.py
Lib/test/test_smtplib.py
Lib/test/test_socket.py
Lib/test/test_ssl.py
Lib/test/test_strptime.py
Lib/test/test_support.py
Lib/test/test_time.py
Lib/test/test_typing.py
Lib/test/test_urllib2net.py
Lib/test/test_urlparse.py
Lib/test/test_venv.py
Lib/test/test_warnings/__init__.py
Lib/test/test_webbrowser.py
Lib/test/test_xml_etree.py
Lib/test/test_xml_etree_c.py
Lib/tkinter/__init__.py
Lib/tkinter/test/support.py
Lib/tkinter/test/test_tkinter/test_widgets.py
Lib/trace.py
Lib/turtle.py
Lib/typing.py
Lib/unittest/mock.py
Lib/unittest/test/testmock/testcallable.py
Lib/unittest/test/testmock/testhelpers.py
Lib/unittest/test/testmock/testmock.py
Lib/unittest/test/testmock/testpatch.py
Lib/urllib/parse.py
Lib/urllib/request.py
Lib/venv/__init__.py
Lib/warnings.py
Lib/webbrowser.py
Lib/xml/dom/domreg.py
Lib/xml/dom/minidom.py
Lib/xml/sax/__init__.py
Lib/zipfile.py
Mac/BuildScript/build-installer.py
Mac/BuildScript/resources/Welcome.rtf
Mac/BuildScript/tk868_on_10_8_10_9.patch [deleted file]
Makefile.pre.in
Misc/ACKS
Misc/NEWS
Modules/Setup.dist
Modules/_abc.c
Modules/_asynciomodule.c
Modules/_bz2module.c
Modules/_collectionsmodule.c
Modules/_csv.c
Modules/_ctypes/_ctypes.c
Modules/_ctypes/callbacks.c
Modules/_ctypes/callproc.c
Modules/_cursesmodule.c
Modules/_datetimemodule.c
Modules/_decimal/libmpdec/mpdecimal.h
Modules/_elementtree.c
Modules/_hashopenssl.c
Modules/_io/bytesio.c
Modules/_io/fileio.c
Modules/_io/textio.c
Modules/_io/winconsoleio.c
Modules/_localemodule.c
Modules/_lzmamodule.c
Modules/_multiprocessing/semaphore.c
Modules/_operator.c
Modules/_pickle.c
Modules/_posixsubprocess.c
Modules/_sqlite/connection.c
Modules/_sqlite/row.c
Modules/_sre.c
Modules/_ssl.c
Modules/_testbuffer.c
Modules/_testcapimodule.c
Modules/_threadmodule.c
Modules/_tkinter.c
Modules/_tracemalloc.c
Modules/arraymodule.c
Modules/cjkcodecs/_codecs_jp.c
Modules/cjkcodecs/multibytecodec.c
Modules/clinic/md5module.c.h
Modules/clinic/posixmodule.c.h
Modules/clinic/sha1module.c.h
Modules/clinic/sha256module.c.h
Modules/clinic/sha512module.c.h
Modules/expat/expat_external.h
Modules/expat/xmltok.c
Modules/faulthandler.c
Modules/fcntlmodule.c
Modules/grpmodule.c
Modules/main.c
Modules/makesetup
Modules/md5module.c
Modules/mmapmodule.c
Modules/nismodule.c
Modules/posixmodule.c
Modules/pwdmodule.c
Modules/pyexpat.c
Modules/readline.c
Modules/selectmodule.c
Modules/sha1module.c
Modules/sha256module.c
Modules/sha512module.c
Modules/socketmodule.c
Modules/termios.c
Modules/timemodule.c
Modules/xxsubtype.c
Modules/zlibmodule.c
Objects/bytearrayobject.c
Objects/bytesobject.c
Objects/capsule.c
Objects/cellobject.c
Objects/classobject.c
Objects/descrobject.c
Objects/exceptions.c
Objects/fileobject.c
Objects/frameobject.c
Objects/funcobject.c
Objects/genobject.c
Objects/memoryobject.c
Objects/methodobject.c
Objects/namespaceobject.c
Objects/object.c
Objects/obmalloc.c
Objects/odictobject.c
Objects/rangeobject.c
Objects/setobject.c
Objects/stringlib/localeutil.h
Objects/typeobject.c
Objects/unicodeobject.c
PC/_findvs.cpp [deleted file]
PC/classicAppCompat.can.xml [new file with mode: 0644]
PC/classicAppCompat.cat [new file with mode: 0644]
PC/classicAppCompat.sccd [new file with mode: 0644]
PC/external/Externals.txt [deleted file]
PC/external/include/Setup.Configuration.h [deleted file]
PC/external/v140/amd64/Microsoft.VisualStudio.Setup.Configuration.Native.lib [deleted file]
PC/external/v140/win32/Microsoft.VisualStudio.Setup.Configuration.Native.lib [deleted file]
PC/external/v141/amd64/Microsoft.VisualStudio.Setup.Configuration.Native.lib [deleted file]
PC/external/v141/win32/Microsoft.VisualStudio.Setup.Configuration.Native.lib [deleted file]
PC/getpathp.c
PC/icons/pythonwx150.png [new file with mode: 0644]
PC/icons/pythonwx44.png [new file with mode: 0644]
PC/icons/pythonx150.png [new file with mode: 0644]
PC/icons/pythonx44.png [new file with mode: 0644]
PC/icons/pythonx50.png [new file with mode: 0644]
PC/launcher.c
PC/layout/__init__.py [new file with mode: 0644]
PC/layout/__main__.py [new file with mode: 0644]
PC/layout/main.py [new file with mode: 0644]
PC/layout/support/__init__.py [new file with mode: 0644]
PC/layout/support/appxmanifest.py [new file with mode: 0644]
PC/layout/support/catalog.py [new file with mode: 0644]
PC/layout/support/constants.py [new file with mode: 0644]
PC/layout/support/distutils.command.bdist_wininst.py [new file with mode: 0644]
PC/layout/support/filesets.py [new file with mode: 0644]
PC/layout/support/logging.py [new file with mode: 0644]
PC/layout/support/options.py [new file with mode: 0644]
PC/layout/support/pip.py [new file with mode: 0644]
PC/layout/support/props.py [new file with mode: 0644]
PC/layout/support/python.props [moved from Tools/nuget/python.props with 100% similarity]
PC/pylauncher.rc
PC/python_uwp.cpp [new file with mode: 0644]
PC/store_info.txt [new file with mode: 0644]
PC/winreg.c
PCbuild/_distutils_findvs.vcxproj.filters [deleted file]
PCbuild/_tkinter.vcxproj
PCbuild/build.bat
PCbuild/find_msbuild.bat
PCbuild/get_externals.bat
PCbuild/pcbuild.proj
PCbuild/pcbuild.sln
PCbuild/prepare_ssl.bat
PCbuild/pyproject.props
PCbuild/python.props
PCbuild/python_uwp.vcxproj [moved from PCbuild/_distutils_findvs.vcxproj with 76% similarity]
PCbuild/pythoncore.vcxproj
PCbuild/pythonw_uwp.vcxproj [new file with mode: 0644]
PCbuild/venvlauncher.vcxproj [new file with mode: 0644]
PCbuild/venvwlauncher.vcxproj [new file with mode: 0644]
Parser/myreadline.c
Parser/tokenizer.c
Programs/_testembed.c
Python/_warnings.c
Python/ast.c
Python/bootstrap_hash.c
Python/ceval.c
Python/codecs.c
Python/context.c
Python/fileutils.c
Python/formatter_unicode.c
Python/import.c
Python/marshal.c
Python/pathconfig.c
Python/peephole.c
Python/pylifecycle.c
Python/pymath.c
Python/pystate.c
Python/pystrtod.c
Python/symtable.c
Python/sysmodule.c
Python/thread_pthread.h
Python/traceback.c
README.rst
Tools/gdb/libpython.py
Tools/msi/buildrelease.bat
Tools/msi/dev/dev.wixproj
Tools/msi/lib/lib_files.wxs
Tools/msi/make_appx.ps1 [new file with mode: 0644]
Tools/msi/make_cat.ps1 [new file with mode: 0644]
Tools/msi/make_zip.proj
Tools/msi/make_zip.py [deleted file]
Tools/msi/sdktools.psm1 [new file with mode: 0644]
Tools/msi/sign_build.ps1 [new file with mode: 0644]
Tools/nuget/make_pkg.proj
Tools/scripts/patchcheck.py
Tools/scripts/texi2html.py
aclocal.m4
configure
configure.ac
setup.py

index c0404ae..492e4e3 100644 (file)
@@ -12,7 +12,7 @@ steps:
   inputs:
     versionSpec: '>=3.6'
 
-- script: python -m pip install sphinx~=1.6.1 blurb python-docs-theme
+- script: python -m pip install sphinx==1.8.2 blurb python-docs-theme
   displayName: 'Install build dependencies'
 
 - ${{ if ne(parameters.latex, 'true') }}:
diff --git a/.azure-pipelines/windows-appx-test.yml b/.azure-pipelines/windows-appx-test.yml
new file mode 100644 (file)
index 0000000..5f3fe6c
--- /dev/null
@@ -0,0 +1,67 @@
+jobs:
+- job: Prebuild
+  displayName: Pre-build checks
+
+  pool:
+    vmImage: ubuntu-16.04
+
+  steps:
+  - template: ./prebuild-checks.yml
+
+
+- job: Windows_Appx_Tests
+  displayName: Windows Appx Tests
+  dependsOn: Prebuild
+  condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true'))
+
+  pool:
+    vmImage: vs2017-win2016
+
+  strategy:
+    matrix:
+      win64:
+        arch: amd64
+        buildOpt: '-p x64'
+        testRunTitle: '$(Build.SourceBranchName)-win64-appx'
+        testRunPlatform: win64
+    maxParallel: 2
+
+  steps:
+  - checkout: self
+    clean: true
+    fetchDepth: 5
+
+  - powershell: |
+      # Relocate build outputs outside of source directory to make cleaning faster
+      Write-Host '##vso[task.setvariable variable=Py_IntDir]$(Build.BinariesDirectory)\obj'
+      # UNDONE: Do not build to a different directory because of broken tests
+      Write-Host '##vso[task.setvariable variable=Py_OutDir]$(Build.SourcesDirectory)\PCbuild'
+      Write-Host '##vso[task.setvariable variable=EXTERNAL_DIR]$(Build.BinariesDirectory)\externals'
+    displayName: Update build locations
+
+  - script: PCbuild\build.bat -e $(buildOpt)
+    displayName: 'Build CPython'
+    env:
+      IncludeUwp: true
+
+  - script: python.bat PC\layout -vv -s "$(Build.SourcesDirectory)" -b "$(Py_OutDir)\$(arch)" -t "$(Py_IntDir)\layout-tmp-$(arch)" --copy "$(Py_IntDir)\layout-$(arch)" --precompile --preset-appx --include-tests
+    displayName: 'Create APPX layout'
+
+  - script: .\python.exe -m test.pythoninfo
+    workingDirectory: $(Py_IntDir)\layout-$(arch)
+    displayName: 'Display build info'
+
+  - script: .\python.exe -m test -q -uall -u-cpu -rwW --slowest --timeout=1200 -j0 --junit-xml="$(Build.BinariesDirectory)\test-results.xml" --tempdir "$(Py_IntDir)\tmp-$(arch)"
+    workingDirectory: $(Py_IntDir)\layout-$(arch)
+    displayName: 'Tests'
+    env:
+      PREFIX: $(Py_IntDir)\layout-$(arch)
+
+  - task: PublishTestResults@2
+    displayName: 'Publish Test Results'
+    inputs:
+      testResultsFiles: '$(Build.BinariesDirectory)\test-results.xml'
+      mergeTestResults: true
+      testRunTitle: $(testRunTitle)
+      platform: $(testRunPlatform)
+    condition: succeededOrFailed()
index d8d5f17..cba0015 100644 (file)
@@ -13,11 +13,13 @@ steps:
 
 - script: PCbuild\build.bat -e $(buildOpt)
   displayName: 'Build CPython'
+  env:
+    IncludeUwp: true
 
 - script: python.bat -m test.pythoninfo
   displayName: 'Display build info'
 
-- script: PCbuild\rt.bat -q -uall -u-cpu -rwW --slowest --timeout=1200 -j0 --junit-xml="$(Build.BinariesDirectory)\test-results.xml"
+- script: PCbuild\rt.bat -q -uall -u-cpu -rwW --slowest --timeout=1200 -j0 --junit-xml="$(Build.BinariesDirectory)\test-results.xml" --tempdir="$(Build.BinariesDirectory)\test"
   displayName: 'Tests'
   env:
     PREFIX: $(Py_OutDir)\$(arch)
index 5a9a46f..33abb5b 100644 (file)
@@ -198,7 +198,7 @@ a buffer, see :c:func:`PyObject_GetBuffer`.
       indicates that no de-referencing should occur (striding in a contiguous
       memory block).
 
-      If all suboffsets are negative (i.e. no de-referencing is needed, then
+      If all suboffsets are negative (i.e. no de-referencing is needed), then
       this field must be NULL (the default value).
 
       This type of array representation is used by the Python Imaging Library
index 9566d9d..c46722d 100644 (file)
@@ -60,7 +60,7 @@ The following functions provide locale-independent string to number conversions.
    The conversion is independent of the current locale.
 
    If ``endptr`` is ``NULL``, convert the whole string.  Raise
-   ValueError and return ``-1.0`` if the string is not a valid
+   :exc:`ValueError` and return ``-1.0`` if the string is not a valid
    representation of a floating-point number.
 
    If endptr is not ``NULL``, convert as much of the string as
index 2bc1bd8..dd1e026 100644 (file)
@@ -186,34 +186,42 @@ NULL pointer for use in a ``return`` statement.
    then it constructs a tuple object whose first item is the *ierr* value and whose
    second item is the corresponding error message (gotten from
    :c:func:`FormatMessage`), and then calls ``PyErr_SetObject(PyExc_WindowsError,
-   object)``. This function always returns *NULL*. Availability: Windows.
+   object)``. This function always returns *NULL*.
+
+   .. availability:: Windows.
 
 
 .. c:function:: PyObject* PyErr_SetExcFromWindowsErr(PyObject *type, int ierr)
 
    Similar to :c:func:`PyErr_SetFromWindowsErr`, with an additional parameter
-   specifying the exception type to be raised. Availability: Windows.
+   specifying the exception type to be raised.
+
+   .. availability:: Windows.
 
 
 .. c:function:: PyObject* PyErr_SetFromWindowsErrWithFilename(int ierr, const char *filename)
 
    Similar to :c:func:`PyErr_SetFromWindowsErrWithFilenameObject`, but the
    filename is given as a C string.  *filename* is decoded from the filesystem
-   encoding (:func:`os.fsdecode`).  Availability: Windows.
+   encoding (:func:`os.fsdecode`).
+
+   .. availability:: Windows.
 
 
 .. c:function:: PyObject* PyErr_SetExcFromWindowsErrWithFilenameObject(PyObject *type, int ierr, PyObject *filename)
 
    Similar to :c:func:`PyErr_SetFromWindowsErrWithFilenameObject`, with an
    additional parameter specifying the exception type to be raised.
-   Availability: Windows.
+
+   .. availability:: Windows.
 
 
 .. c:function:: PyObject* PyErr_SetExcFromWindowsErrWithFilenameObjects(PyObject *type, int ierr, PyObject *filename, PyObject *filename2)
 
    Similar to :c:func:`PyErr_SetExcFromWindowsErrWithFilenameObject`,
    but accepts a second filename object.
-   Availability: Windows.
+
+   .. availability:: Windows.
 
    .. versionadded:: 3.4
 
@@ -221,7 +229,9 @@ NULL pointer for use in a ``return`` statement.
 .. c:function:: PyObject* PyErr_SetExcFromWindowsErrWithFilename(PyObject *type, int ierr, const char *filename)
 
    Similar to :c:func:`PyErr_SetFromWindowsErrWithFilename`, with an additional
-   parameter specifying the exception type to be raised. Availability: Windows.
+   parameter specifying the exception type to be raised.
+
+   .. availability:: Windows.
 
 
 .. c:function:: PyObject* PyErr_SetImportError(PyObject *msg, PyObject *name, PyObject *path)
index 7f54b6a..472cd93 100644 (file)
@@ -66,6 +66,9 @@ Constructors for container types must conform to two rules:
    A macro version of :c:func:`PyObject_GC_Track`.  It should not be used for
    extension modules.
 
+   .. deprecated:: 3.6
+      This macro is removed from Python 3.8.
+
 Similarly, the deallocator for the object must conform to a similar pair of
 rules:
 
@@ -95,6 +98,9 @@ rules:
    A macro version of :c:func:`PyObject_GC_UnTrack`.  It should not be used for
    extension modules.
 
+   .. deprecated:: 3.6
+      This macro is removed from Python 3.8.
+
 The :c:member:`~PyTypeObject.tp_traverse` handler accepts a function parameter of this type:
 
 
index 694b466..50998be 100644 (file)
@@ -156,7 +156,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2.
 
    See :pep:`529` for more details.
 
-   Availability: Windows.
+   .. availability:: Windows.
 
 .. c:var:: Py_LegacyWindowsStdioFlag
 
@@ -168,7 +168,7 @@ to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2.
 
    See :pep:`528` for more details.
 
-   Availability: Windows.
+   .. availability:: Windows.
 
 .. c:var:: Py_NoSiteFlag
 
index 9f7b13c..b79b7e4 100644 (file)
@@ -342,7 +342,7 @@ Configuration                    Name                  PyMem_RawMalloc     PyMem
 Release build                    ``"pymalloc"``        ``malloc``          ``pymalloc``           ``pymalloc``
 Debug build                      ``"pymalloc_debug"``  ``malloc`` + debug  ``pymalloc`` + debug   ``pymalloc`` + debug
 Release build, without pymalloc  ``"malloc"``          ``malloc``          ``malloc``             ``malloc``
-Release build, without pymalloc  ``"malloc_debug"``    ``malloc`` + debug  ``malloc`` + debug     ``malloc`` + debug
+Debug build, without pymalloc    ``"malloc_debug"``    ``malloc`` + debug  ``malloc`` + debug     ``malloc`` + debug
 ===============================  ====================  ==================  =====================  ====================
 
 Legend:
index 797b904..da45da1 100644 (file)
@@ -292,7 +292,8 @@ definition with the same method name.
 
    :attr:`flags` can be ``0`` for write and read access or :c:macro:`READONLY` for
    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`
+   :c:macro:`READONLY`.  :c:macro:`T_STRING` data is interpreted as UTF-8.
+   Only :c:macro:`T_OBJECT` and :c:macro:`T_OBJECT_EX`
    members can be deleted.  (They are set to *NULL*).
 
 
index 60c5e73..4dfd53f 100644 (file)
@@ -35,7 +35,7 @@ Type Objects
 
    Clear the internal lookup cache. Return the current version tag.
 
-.. c:function:: long PyType_GetFlags(PyTypeObject* type)
+.. c:function:: unsigned long PyType_GetFlags(PyTypeObject* type)
 
    Return the :c:member:`~PyTypeObject.tp_flags` member of *type*. This function is primarily
    meant for use with `Py_LIMITED_API`; the individual flag bits are
@@ -44,6 +44,9 @@ Type Objects
 
    .. versionadded:: 3.2
 
+   .. versionchanged:: 3.4
+      The return type is now ``unsigned long`` rather than ``long``.
+
 
 .. c:function:: void PyType_Modified(PyTypeObject *type)
 
index cefe9d4..c891f63 100644 (file)
@@ -306,7 +306,7 @@ the same library that the Python runtime is using.
    Evaluate a precompiled code object, given a particular environment for its
    evaluation.  This environment consists of a dictionary of global variables,
    a mapping object of local variables, arrays of arguments, keywords and
-   defaults, a dictionary of default values for :ref:`keyword-only\
+   defaults, a dictionary of default values for :ref:`keyword-only
    <keyword-only_parameter>` arguments and a closure tuple of cells.
 
 
index b7bb5a0..eab3c39 100644 (file)
@@ -34,13 +34,18 @@ today_fmt = '%B %d, %Y'
 # By default, highlight as Python 3.
 highlight_language = 'python3'
 
-# Require Sphinx 1.2 for build.
-needs_sphinx = '1.2'
+# Require Sphinx 1.6.6 for build.
+needs_sphinx = "1.6.6"
 
 # Ignore any .rst files in the venv/ directory.
 venvdir = os.getenv('VENVDIR', 'venv')
 exclude_patterns = [venvdir+'/*', 'README.rst']
 
+# Disable Docutils smartquotes for several translations
+smartquotes_excludes = {
+    'languages': ['ja', 'fr', 'zh_TW', 'zh_CN'], 'builders': ['man', 'text'],
+}
+
 
 # Options for HTML output
 # -----------------------
index 3c89468..8efeffb 100644 (file)
@@ -183,8 +183,9 @@ the full reference.
    | *sources*              | list of source filenames,      | a list of strings         |
    |                        | relative to the distribution   |                           |
    |                        | root (where the setup script   |                           |
-   |                        | lives), in Unix form (slash-   |                           |
-   |                        | separated) for portability.    |                           |
+   |                        | lives), in Unix form           |                           |
+   |                        | (slash-separated) for          |                           |
+   |                        | portability.                   |                           |
    |                        | Source files may be C, C++,    |                           |
    |                        | SWIG (.i), platform-specific   |                           |
    |                        | resource files, or whatever    |                           |
@@ -940,7 +941,7 @@ timestamp dependency analysis.
 .. function:: newer_group(sources, target[, missing='error'])
 
    Return true if *target* is out-of-date with respect to any file listed in
-   *sources*  In other words, if *target* exists and is newer than every file in
+   *sources*.  In other words, if *target* exists and is newer than every file in
    *sources*, return false; otherwise return true. *missing* controls what we do
    when a source file is missing; the default (``'error'``) is to blow up with an
    :exc:`OSError` from  inside :func:`os.stat`; if it is ``'ignore'``, we silently
@@ -1566,8 +1567,8 @@ lines, and joining lines with backslashes.
    +------------------+--------------------------------+---------+
    | option name      | description                    | default |
    +==================+================================+=========+
-   | *strip_comments* | strip from ``'#'`` to end-of-  | true    |
-   |                  | line, as well as any           |         |
+   | *strip_comments* | strip from ``'#'`` to          | true    |
+   |                  | end-of-line, as well as any    |         |
    |                  | whitespace leading up to the   |         |
    |                  | ``'#'``\ ---unless it is       |         |
    |                  | escaped by a backslash         |         |
index 758bd14..f1f3471 100644 (file)
@@ -63,9 +63,9 @@ distribution to generate: for example, ::
 
    python setup.py bdist --format=zip
 
-would, when run on a Unix system, create :file:`Distutils-1.0.{plat}.zip`\
----again, this archive would be unpacked from the root directory to install the
-Distutils.
+would, when run on a Unix system, create
+:file:`Distutils-1.0.{plat}.zip`\ ---again, this archive would be unpacked
+from the root directory to install the Distutils.
 
 The available formats for built distributions are:
 
index cd10a7f..0874d05 100644 (file)
@@ -13,8 +13,8 @@ edit is a cheap and easy way to solicit it.  Configuration files also let you
 provide default values for any command option, which the installer can then
 override either on the command-line or by editing the config file.
 
-The setup configuration file is a useful middle-ground between the setup script
----which, ideally, would be opaque to installers [#]_---and the command-line to
+The setup configuration file is a useful middle-ground between the setup
+script---which, ideally, would be opaque to installers [#]_---and the command-line to
 the setup script, which is outside of your control and entirely up to the
 installer.  In fact, :file:`setup.cfg` (and any other Distutils configuration
 files present on the target system) are processed after the contents of the
index 1d96acb..c1051d2 100644 (file)
@@ -682,9 +682,8 @@ information is sometimes used to indicate sub-releases.  These are
           )
 
 .. versionchanged:: 3.7
-   :class:`~distutils.core.setup` now raises a :exc:`TypeError` if
-   ``classifiers``, ``keywords`` and ``platforms`` fields are not specified
-   as a list.
+   :class:`~distutils.core.setup` now warns when ``classifiers``, ``keywords``
+   or ``platforms`` fields are not specified as a list or a string.
 
 .. _debug-setup-script:
 
diff --git a/Doc/docutils.conf b/Doc/docutils.conf
deleted file mode 100644 (file)
index bda4f5d..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-[restructuredtext parser]
-smartquotes-locales: ja: ""''
index 82b689e..b788a55 100644 (file)
@@ -545,8 +545,9 @@ or more format codes between parentheses.  For example::
 :c:func:`PyObject_CallObject` returns a Python object pointer: this is the return
 value of the Python function.  :c:func:`PyObject_CallObject` is
 "reference-count-neutral" with respect to its arguments.  In the example a new
-tuple was created to serve as the argument list, which is :c:func:`Py_DECREF`\
--ed immediately after the :c:func:`PyObject_CallObject` call.
+tuple was created to serve as the argument list, which is
+:c:func:`Py_DECREF`\ -ed immediately after the :c:func:`PyObject_CallObject`
+call.
 
 The return value of :c:func:`PyObject_CallObject` is "new": either it is a brand
 new object, or it is an existing object whose reference count has been
index 5e54df6..e2d63a0 100644 (file)
@@ -528,7 +528,7 @@ Some unacceptable solutions that have been proposed:
      mydict = {[1, 2]: '12'}
      print(mydict[[1, 2]])
 
-  would raise a KeyError exception because the id of the ``[1, 2]`` used in the
+  would raise a :exc:`KeyError` exception because the id of the ``[1, 2]`` used in the
   second line differs from that in the first line.  In other words, dictionary
   keys should be compared using ``==``, not using :keyword:`is`.
 
index fd04a83..74e1af6 100644 (file)
@@ -63,7 +63,7 @@ How can I execute arbitrary Python statements from C?
 The highest-level function to do this is :c:func:`PyRun_SimpleString` which takes
 a single string argument to be executed in the context of the module
 ``__main__`` and returns ``0`` for success and ``-1`` when an exception occurred
-(including ``SyntaxError``).  If you want more control, use
+(including :exc:`SyntaxError`).  If you want more control, use
 :c:func:`PyRun_String`; see the source for :c:func:`PyRun_SimpleString` in
 ``Python/pythonrun.c``.
 
@@ -277,7 +277,7 @@ However sometimes you have to run the embedded Python interpreter in the same
 thread as your rest application and you can't allow the
 :c:func:`PyRun_InteractiveLoop` to stop while waiting for user input.  The one
 solution then is to call :c:func:`PyParser_ParseString` and test for ``e.error``
-equal to ``E_EOF``, which means the input is incomplete).  Here's a sample code
+equal to ``E_EOF``, which means the input is incomplete.  Here's a sample code
 fragment, untested, inspired by code from Alex Farber::
 
    #include <Python.h>
index 7792cfa..a46d4c1 100644 (file)
@@ -23,23 +23,10 @@ This is not necessarily a straightforward question. If you are already familiar
 with running programs from the Windows command line then everything will seem
 obvious; otherwise, you might need a little more guidance.
 
-.. sidebar:: |Python Development on XP|_
-   :subtitle: `Python Development on XP`_
-
-   This series of screencasts aims to get you up and running with Python on
-   Windows XP.  The knowledge is distilled into 1.5 hours and will get you up
-   and running with the right Python distribution, coding in your choice of IDE,
-   and debugging and writing solid code with unit-tests.
-
-.. |Python Development on XP| image:: python-video-icon.png
-.. _`Python Development on XP`:
-   http://showmedo.com/videotutorials/series?name=pythonOzsvaldPyNewbieSeries
-
 Unless you use some sort of integrated development environment, you will end up
 *typing* Windows commands into what is variously referred to as a "DOS window"
 or "Command prompt window".  Usually you can create such a window from your
-Start menu; under Windows 7 the menu selection is :menuselection:`Start -->
-Programs --> Accessories --> Command Prompt`.  You should be able to recognize
+search bar by searching for ``cmd``.  You should be able to recognize
 when you have started such a window because you will see a Windows "command
 prompt", which usually looks like this:
 
@@ -64,19 +51,19 @@ compiles it into bytecodes, and then executes the bytecodes to run your
 program. So, how do you arrange for the interpreter to handle your Python?
 
 First, you need to make sure that your command window recognises the word
-"python" as an instruction to start the interpreter.  If you have opened a
-command window, you should try entering the command ``python`` and hitting
+"py" as an instruction to start the interpreter.  If you have opened a
+command window, you should try entering the command ``py`` and hitting
 return:
 
 .. code-block:: doscon
 
-   C:\Users\YourName> python
+   C:\Users\YourName> py
 
 You should then see something like:
 
 .. code-block:: pycon
 
-   Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:55:48) [MSC v.1600 32 bit (Intel)] on win32
+   Python 3.6.4 (v3.6.4:d48eceb, Dec 19 2017, 06:04:45) [MSC v.1900 32 bit (Intel)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>>
 
@@ -93,64 +80,33 @@ by entering a few expressions of your choice and seeing the results:
     'HelloHelloHello'
 
 Many people use the interactive mode as a convenient yet highly programmable
-calculator.  When you want to end your interactive Python session, hold the :kbd:`Ctrl`
-key down while you enter a :kbd:`Z`, then hit the ":kbd:`Enter`" key to get back to your
-Windows command prompt.
+calculator.  When you want to end your interactive Python session,
+call the :func:`exit` function or hold the :kbd:`Ctrl` key down
+while you enter a :kbd:`Z`, then hit the ":kbd:`Enter`" key to get
+back to your Windows command prompt.
 
 You may also find that you have a Start-menu entry such as :menuselection:`Start
---> Programs --> Python 3.3 --> Python (command line)` that results in you
+--> Programs --> Python 3.x --> Python (command line)` that results in you
 seeing the ``>>>`` prompt in a new window.  If so, the window will disappear
-after you enter the :kbd:`Ctrl-Z` character; Windows is running a single "python"
+after you call the :func:`exit` function or enter the :kbd:`Ctrl-Z`
+character; Windows is running a single "python"
 command in the window, and closes it when you terminate the interpreter.
 
-If the ``python`` command, instead of displaying the interpreter prompt ``>>>``,
-gives you a message like::
-
-   'python' is not recognized as an internal or external command, operable program or batch file.
-
-.. sidebar:: |Adding Python to DOS Path|_
-   :subtitle: `Adding Python to DOS Path`_
-
-   Python is not added to the DOS path by default.  This screencast will walk
-   you through the steps to add the correct entry to the `System Path`, allowing
-   Python to be executed from the command-line by all users.
-
-.. |Adding Python to DOS Path| image:: python-video-icon.png
-.. _`Adding Python to DOS Path`:
-   http://showmedo.com/videotutorials/video?name=960000&fromSeriesID=96
-
-
-or::
-
-   Bad command or filename
-
-then you need to make sure that your computer knows where to find the Python
-interpreter.  To do this you will have to modify a setting called PATH, which is
-a list of directories where Windows will look for programs.
-
-You should arrange for Python's installation directory to be added to the PATH
-of every command window as it starts.  If you installed Python fairly recently
-then the command ::
-
-   dir C:\py*
+Now that we know the ``py`` command is recognized, you can give your
+Python script to it. You'll have to give either an absolute or a
+relative path to the Python script. Let's say your Python script is
+located in your desktop and is named ``hello.py``, and your command
+prompt is nicely opened in your home directory so you're seeing something
+similar to::
 
-will probably tell you where it is installed; the usual location is something
-like ``C:\Python33``.  Otherwise you will be reduced to a search of your whole
-disk ... use :menuselection:`Tools --> Find` or hit the :guilabel:`Search`
-button and look for "python.exe".  Supposing you discover that Python is
-installed in the ``C:\Python33`` directory (the default at the time of writing),
-you should make sure that entering the command ::
+   C:\Users\YourName>
 
-   c:\Python33\python
+So now you'll ask the ``py`` command to give your script to Python by
+typing ``py`` followed by your script path::
 
-starts up the interpreter as above (and don't forget you'll need a ":kbd:`Ctrl-Z`" and
-an ":kbd:`Enter`" to get out of it). Once you have verified the directory, you can
-add it to the system path to make it easier to start Python by just running
-the ``python`` command. This is currently an option in the installer as of
-CPython 3.3.
 
-More information about environment variables can be found on the
-:ref:`Using Python on Windows <setting-envvars>` page.
+   C:\Users\YourName> py Desktop\hello.py
+   hello
 
 How do I make Python scripts executable?
 ----------------------------------------
@@ -324,36 +280,3 @@ How do I check for a keypress without blocking?
 Use the msvcrt module.  This is a standard Windows-specific extension module.
 It defines a function ``kbhit()`` which checks whether a keyboard hit is
 present, and ``getch()`` which gets one character without echoing it.
-
-
-How do I emulate os.kill() in Windows?
---------------------------------------
-
-Prior to Python 2.7 and 3.2, to terminate a process, you can use :mod:`ctypes`:
-
-.. code-block:: python
-
-   import ctypes
-
-   def kill(pid):
-       """kill function for Win32"""
-       kernel32 = ctypes.windll.kernel32
-       handle = kernel32.OpenProcess(1, 0, pid)
-       return (0 != kernel32.TerminateProcess(handle, 0))
-
-In 2.7 and 3.2, :func:`os.kill` is implemented similar to the above function,
-with the additional feature of being able to send :kbd:`Ctrl+C` and :kbd:`Ctrl+Break`
-to console subprocesses which are designed to handle those signals. See
-:func:`os.kill` for further details.
-
-How do I extract the downloaded documentation on Windows?
----------------------------------------------------------
-
-Sometimes, when you download the documentation package to a Windows machine
-using a web browser, the file extension of the saved file ends up being .EXE.
-This is a mistake; the extension should be .TGZ.
-
-Simply rename the downloaded file to have the .TGZ extension, and WinZip will be
-able to handle it.  (If your copy of WinZip doesn't, get a newer one from
-https://www.winzip.com.)
-
index 16fc7f0..b8d98dd 100644 (file)
@@ -95,7 +95,7 @@ Glossary
       that it contains :keyword:`yield` expressions for producing a series of
       values usable in an :keyword:`async for` loop.
 
-      Usually refers to a asynchronous generator function, but may refer to an
+      Usually refers to an asynchronous generator function, but may refer to an
       *asynchronous generator iterator* in some contexts.  In cases where the
       intended meaning isn't clear, using the full terms avoids ambiguity.
 
@@ -108,8 +108,8 @@ Glossary
 
       This is an :term:`asynchronous iterator` which when called using the
       :meth:`__anext__` method returns an awaitable object which will execute
-      that the body of the asynchronous generator function until the
-      next :keyword:`yield` expression.
+      the body of the asynchronous generator function until the next
+      :keyword:`yield` expression.
 
       Each :keyword:`yield` temporarily suspends processing, remembering the
       location execution state (including local variables and pending
@@ -200,7 +200,7 @@ Glossary
       ``int(3.15)`` converts the floating point number to the integer ``3``, but
       in ``3+4.5``, each argument is of a different type (one int, one float),
       and both must be converted to the same type before they can be added or it
-      will raise a ``TypeError``.  Without coercion, all arguments of even
+      will raise a :exc:`TypeError`.  Without coercion, all arguments of even
       compatible types would have to be normalized to the same value by the
       programmer, e.g., ``float(3)+4.5`` rather than just ``3+4.5``.
 
@@ -389,7 +389,7 @@ Glossary
       An :term:`annotation` of a function parameter or return value.
 
       Function annotations are usually used for
-      :term:`type hints <type hint>`: for example this function is expected to take two
+      :term:`type hints <type hint>`: for example, this function is expected to take two
       :class:`int` arguments and is also expected to have an :class:`int`
       return value::
 
index 47b5c68..2a2282e 100644 (file)
@@ -971,7 +971,7 @@ provided:
 
 The :class:`NullHandler`, :class:`StreamHandler` and :class:`FileHandler`
 classes are defined in the core logging package. The other handlers are
-defined in a sub- module, :mod:`logging.handlers`. (There is also another
+defined in a sub-module, :mod:`logging.handlers`. (There is also another
 sub-module, :mod:`logging.config`, for configuration functionality.)
 
 Logged messages are formatted for presentation through instances of the
index 92cdf2f..f6a8cd6 100644 (file)
@@ -554,10 +554,10 @@ C headers              ``--install-headers``
 
 These override options can be relative, absolute,
 or explicitly defined in terms of one of the installation base directories.
-(There are two installation base directories, and they are normally the same---
-they only differ when you use the Unix "prefix scheme" and supply different
-``--prefix`` and ``--exec-prefix`` options; using ``--install-lib`` will
-override values computed or given for ``--install-purelib`` and
+(There are two installation base directories, and they are normally the
+same---they only differ when you use the Unix "prefix scheme" and supply
+different ``--prefix`` and ``--exec-prefix`` options; using ``--install-lib``
+will override values computed or given for ``--install-purelib`` and
 ``--install-platlib``, and is recommended for schemes that don't make a
 difference between Python and extension modules.)
 
@@ -584,10 +584,10 @@ in this case.)
 
 If you maintain Python on Windows, you might want third-party modules to live in
 a subdirectory of :file:`{prefix}`, rather than right in :file:`{prefix}`
-itself.  This is almost as easy as customizing the script installation directory
----you just have to remember that there are two types of modules to worry about,
-Python and extension modules, which can conveniently be both controlled by one
-option::
+itself.  This is almost as easy as customizing the script installation
+directory---you just have to remember that there are two types of modules
+to worry about, Python and extension modules, which can conveniently be both
+controlled by one option::
 
    python setup.py install --install-lib=Site
 
index 7a69869..8af828b 100644 (file)
@@ -48,7 +48,7 @@ Key terms
   repository of open source licensed packages made available for use by
   other Python users.
 * the `Python Packaging Authority
-  <https://www.pypa.io/>`__ are the group of
+  <https://www.pypa.io/>`__ is the group of
   developers and documentation authors responsible for the maintenance and
   evolution of the standard packaging tools and the associated metadata and
   file format standards. They maintain a variety of tools, documentation,
index 67cb709..acffabf 100644 (file)
@@ -101,7 +101,8 @@ This module defines the following constants and functions:
    memory page size - platform documentation should be referred to for more
    information (4 KiB pages are common; using multiples of 4096 for the stack size is
    the suggested approach in the absence of more specific information).
-   Availability: Windows, systems with POSIX threads.
+
+   .. availability:: Windows, systems with POSIX threads.
 
 
 .. data:: TIMEOUT_MAX
index 7071076..fc39a2e 100644 (file)
@@ -18,10 +18,10 @@ see the PEP for why this was added to Python. (See also :pep:`3141` and the
 :mod:`numbers` module regarding a type hierarchy for numbers based on ABCs.)
 
 The :mod:`collections` module has some concrete classes that derive from
-ABCs; these can, of course, be further derived. In addition the
+ABCs; these can, of course, be further derived. In addition, the
 :mod:`collections.abc` submodule has some ABCs that can be used to test whether
-a class or instance provides a particular interface, for example, is it
-hashable or a mapping.
+a class or instance provides a particular interface, for example, if it is
+hashable or if it is a mapping.
 
 
 This module provides the metaclass :class:`ABCMeta` for defining ABCs and
@@ -217,7 +217,7 @@ The :mod:`abc` module also provides the following decorator:
    the descriptor must identify itself as abstract using
    :attr:`__isabstractmethod__`. In general, this attribute should be ``True``
    if any of the methods used to compose the descriptor are abstract. For
-   example, Python's built-in property does the equivalent of::
+   example, Python's built-in :class:`property` does the equivalent of::
 
       class Descriptor:
           ...
index 1973afc..cef197f 100644 (file)
@@ -844,6 +844,8 @@ values are:
   Note that ``nargs=1`` produces a list of one item.  This is different from
   the default, in which the item is produced by itself.
 
+.. index:: single: ? (question mark); in argparse module
+
 * ``'?'``. One argument will be consumed from the command line if possible, and
   produced as a single item.  If no command-line argument is present, the value from
   default_ will be produced.  Note that for optional arguments, there is an
@@ -876,6 +878,8 @@ values are:
      Namespace(infile=<_io.TextIOWrapper name='<stdin>' encoding='UTF-8'>,
                outfile=<_io.TextIOWrapper name='<stdout>' encoding='UTF-8'>)
 
+.. index:: single: * (asterisk); in argparse module
+
 * ``'*'``.  All command-line arguments present are gathered into a list.  Note that
   it generally doesn't make much sense to have more than one positional argument
   with ``nargs='*'``, but multiple optional arguments with ``nargs='*'`` is
@@ -888,6 +892,8 @@ values are:
      >>> parser.parse_args('a b --foo x y --bar 1 2'.split())
      Namespace(bar=['1', '2'], baz=['a', 'b'], foo=['x', 'y'])
 
+.. index:: single: + (plus); in argparse module
+
 * ``'+'``. Just like ``'*'``, all command-line args present are gathered into a
   list.  Additionally, an error message will be generated if there wasn't at
   least one command-line argument present.  For example::
index 9ff422c..3c73f74 100644 (file)
@@ -41,6 +41,9 @@ Node classes
    with alternatives (aka "sums"), the left-hand side class is abstract: only
    instances of specific constructor nodes are ever created.
 
+   .. index:: single: ? (question mark); in AST grammar
+   .. index:: single: * (asterisk); in AST grammar
+
    .. attribute:: _fields
 
       Each concrete class has an attribute :attr:`_fields` which gives the names
index 5f926fc..b728803 100644 (file)
@@ -50,7 +50,7 @@ When the debug mode is enabled:
   <asyncio-coroutine-not-scheduled>` and logs them; this mitigates
   the "forgotten await" pitfall.
 
-* Many non-treadsafe asyncio APIs (such as :meth:`loop.call_soon` and
+* Many non-threadsafe asyncio APIs (such as :meth:`loop.call_soon` and
   :meth:`loop.call_at` methods) raise an exception if they are called
   from a wrong thread.
 
index a61b971..647b7fc 100644 (file)
@@ -137,7 +137,7 @@ Running and stopping the loop
 
    Close the event loop.
 
-   The loop must be running when this function is called.
+   The loop must not be running when this function is called.
    Any pending callbacks will be discarded.
 
    This method clears all queues and shuts down the executor, but does
@@ -504,7 +504,7 @@ Opening network connections
    See the documentation of the :meth:`loop.create_connection` method
    for information about arguments to this method.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.7
 
@@ -624,7 +624,7 @@ Creating network servers
    See the documentation of the :meth:`loop.create_server` method
    for information about arguments to this method.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.7
 
@@ -750,7 +750,7 @@ Watching file descriptors
    writing.
 
    Use :func:`functools.partial` :ref:`to pass keyword arguments
-   <asyncio-pass-keywords>` to *func*.
+   <asyncio-pass-keywords>` to *callback*.
 
 .. method:: loop.remove_writer(fd)
 
@@ -964,7 +964,7 @@ Unix signals
    Raise :exc:`RuntimeError` if there is a problem setting up the handler.
 
    Use :func:`functools.partial` :ref:`to pass keyword arguments
-   <asyncio-pass-keywords>` to *func*.
+   <asyncio-pass-keywords>` to *callback*.
 
 .. method:: loop.remove_signal_handler(sig)
 
@@ -973,7 +973,7 @@ Unix signals
    Return ``True`` if the signal handler was removed, or ``False`` if
    no handler was set for the given signal.
 
-Availability: Unix.
+   .. availability:: Unix.
 
 .. seealso::
 
@@ -1417,14 +1417,14 @@ on all platforms.
       asyncio.set_event_loop(loop)
 
 
-   Availability: Unix, Windows.
+   .. availability:: Unix, Windows.
 
 
 .. class:: ProactorEventLoop
 
    An event loop for Windows that uses "I/O Completion Ports" (IOCP).
 
-   Availability: Windows.
+   .. availability:: Windows.
 
    An example how to use :class:`ProactorEventLoop` on Windows::
 
index 42f936d..cab2593 100644 (file)
@@ -103,7 +103,7 @@ asyncio ships with the following built-in policies:
    An alternative event loop policy that uses the
    :class:`ProactorEventLoop` event loop implementation.
 
-   Availability: Windows.
+   .. availability:: Windows.
 
 
 Process Watchers
index bdfdcf7..f54dece 100644 (file)
@@ -717,7 +717,7 @@ received data, and close the connection::
     import asyncio
 
 
-    class EchoServerClientProtocol(asyncio.Protocol):
+    class EchoServerProtocol(asyncio.Protocol):
         def connection_made(self, transport):
             peername = transport.get_extra_info('peername')
             print('Connection from {}'.format(peername))
@@ -740,7 +740,7 @@ received data, and close the connection::
         loop = asyncio.get_running_loop()
 
         server = await loop.create_server(
-            lambda: EchoServerClientProtocol(),
+            lambda: EchoServerProtocol(),
             '127.0.0.1', 8888)
 
         async with server:
index c543aa6..42b4c1f 100644 (file)
@@ -115,7 +115,7 @@ and work with streams:
 
    See also the documentation of :meth:`loop.create_unix_connection`.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.7
 
@@ -137,7 +137,7 @@ and work with streams:
 
    See also the documentation of :meth:`loop.create_unix_server`.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.7
 
index 3168f47..376de8f 100644 (file)
@@ -57,12 +57,12 @@ To actually run a coroutine asyncio provides three main mechanisms:
           print(what)
 
       async def main():
-          print('started at', time.strftime('%X'))
+          print(f"started at {time.strftime('%X')}")
 
           await say_after(1, 'hello')
           await say_after(2, 'world')
 
-          print('finished at', time.strftime('%X'))
+          print(f"finished at {time.strftime('%X')}")
 
       asyncio.run(main())
 
@@ -86,14 +86,14 @@ To actually run a coroutine asyncio provides three main mechanisms:
           task2 = asyncio.create_task(
               say_after(2, 'world'))
 
-          print('started at', time.strftime('%X'))
+          print(f"started at {time.strftime('%X')}")
 
           # Wait until both tasks are completed (should take
           # around 2 seconds.)
           await task1
           await task2
 
-          print('finished at', time.strftime('%X'))
+          print(f"finished at {time.strftime('%X')}")
 
   Note that expected output now shows that the snippet runs
   1 second faster than before::
@@ -597,9 +597,9 @@ Scheduling From Other Threads
          print('The coroutine took too long, cancelling the task...')
          future.cancel()
      except Exception as exc:
-         print('The coroutine raised an exception: {!r}'.format(exc))
+         print(f'The coroutine raised an exception: {exc!r}')
      else:
-         print('The coroutine returned: {!r}'.format(result))
+         print(f'The coroutine returned: {result!r}')
 
    See the :ref:`concurrency and multithreading <asyncio-multithreading>`
    section of the documentation.
index 11d3616..a86518e 100644 (file)
@@ -273,14 +273,18 @@ any that have been added to the map during asynchronous service) is closed.
    with an optional map argument and wraps it for use with the :c:func:`poll`
    or :c:func:`loop` functions.  If provided a file object or anything with a
    :c:func:`fileno` method, that method will be called and passed to the
-   :class:`file_wrapper` constructor.  Availability: UNIX.
+   :class:`file_wrapper` constructor.
+
+   .. availability:: Unix.
 
 .. class:: file_wrapper()
 
    A file_wrapper takes an integer file descriptor and calls :func:`os.dup` to
    duplicate the handle so that the original handle may be closed independently
    of the file_wrapper.  This class implements sufficient methods to emulate a
-   socket for use by the :class:`file_dispatcher` class.  Availability: UNIX.
+   socket for use by the :class:`file_dispatcher` class.
+
+   .. availability:: Unix.
 
 
 .. _asyncore-example-1:
index 3b4a8ff..d57edb7 100644 (file)
@@ -61,6 +61,10 @@ A :class:`Cmd` instance has the following methods:
 
    An end-of-file on input is passed back as the string ``'EOF'``.
 
+   .. index::
+      single: ? (question mark); in a command interpreter
+      single: ! (exclamation); in a command interpreter
+
    An interpreter instance will recognize a command name ``foo`` if and only if it
    has a method :meth:`do_foo`.  As a special case, a line beginning with the
    character ``'?'`` is dispatched to the method :meth:`do_help`.  As another
index 24008a0..ef788bf 100644 (file)
@@ -312,6 +312,14 @@ defined and implemented by all standard Python codecs:
 The following error handlers are only applicable to
 :term:`text encodings <text encoding>`:
 
+.. index::
+   single: ? (question mark); replacement character
+   single: \ (backslash); escape sequence
+   single: \x; escape sequence
+   single: \u; escape sequence
+   single: \U; escape sequence
+   single: \N; escape sequence
+
 +-------------------------+-----------------------------------------------+
 | Value                   | Meaning                                       |
 +=========================+===============================================+
@@ -1113,10 +1121,10 @@ particular, the following variants typically exist:
 |                 | ks_c-5601, ks_c-5601-1987,     |                                |
 |                 | ksx1001, ks_x-1001             |                                |
 +-----------------+--------------------------------+--------------------------------+
-| gb2312          | chinese, csiso58gb231280, euc- | Simplified Chinese             |
-|                 | cn, euccn, eucgb2312-cn,       |                                |
-|                 | gb2312-1980, gb2312-80, iso-   |                                |
-|                 | ir-58                          |                                |
+| gb2312          | chinese, csiso58gb231280,      | Simplified Chinese             |
+|                 | euc-cn, euccn, eucgb2312-cn,   |                                |
+|                 | gb2312-1980, gb2312-80,        |                                |
+|                 | iso-ir-58                      |                                |
 +-----------------+--------------------------------+--------------------------------+
 | gbk             | 936, cp936, ms936              | Unified Chinese                |
 +-----------------+--------------------------------+--------------------------------+
@@ -1471,7 +1479,7 @@ functions can be used directly if desired.
 
 Encode operand according to the ANSI codepage (CP_ACP).
 
-Availability: Windows only.
+.. availability:: Windows only.
 
 .. versionchanged:: 3.3
    Support any error handler.
index 495cfc2..016e56c 100644 (file)
@@ -225,7 +225,7 @@ For example::
 .. class:: Counter([iterable-or-mapping])
 
     A :class:`Counter` is a :class:`dict` subclass for counting hashable objects.
-    It is an unordered collection where elements are stored as dictionary keys
+    It is a collection where elements are stored as dictionary keys
     and their counts are stored as dictionary values.  Counts are allowed to be
     any integer value including zero or negative counts.  The :class:`Counter`
     class is similar to bags or multisets in other languages.
index 7b3963d..5151f3a 100644 (file)
@@ -85,13 +85,16 @@ compile Python sources.
 
 .. cmdoption:: --invalidation-mode [timestamp|checked-hash|unchecked-hash]
 
-   Control how the generated pycs will be invalidated at runtime. The default
-   setting, ``timestamp``, means that ``.pyc`` files with the source timestamp
+   Control how the generated byte-code files are invalidated at runtime.
+   The ``timestamp`` value, means that ``.pyc`` files with the source timestamp
    and size embedded will be generated. The ``checked-hash`` and
    ``unchecked-hash`` values cause hash-based pycs to be generated. Hash-based
    pycs embed a hash of the source file contents rather than a timestamp. See
-   :ref:`pyc-invalidation` for more information on how Python validates bytecode
-   cache files at runtime.
+   :ref:`pyc-invalidation` for more information on how Python validates
+   bytecode cache files at runtime.
+   The default is ``timestamp`` if the :envvar:`SOURCE_DATE_EPOCH` environment
+   variable is not set, and ``checked-hash`` if the ``SOURCE_DATE_EPOCH``
+   environment variable is set.
 
 .. versionchanged:: 3.2
    Added the ``-i``, ``-b`` and ``-h`` options.
index b1b086b..a574915 100644 (file)
@@ -141,7 +141,7 @@ And::
    each worker thread; *initargs* is a tuple of arguments passed to the
    initializer.  Should *initializer* raise an exception, all currently
    pending jobs will raise a :exc:`~concurrent.futures.thread.BrokenThreadPool`,
-   as well any attempt to submit more jobs to the pool.
+   as well as any attempt to submit more jobs to the pool.
 
    .. versionchanged:: 3.5
       If *max_workers* is ``None`` or
@@ -153,7 +153,7 @@ And::
 
    .. versionadded:: 3.6
       The *thread_name_prefix* argument was added to allow users to
-      control the threading.Thread names for worker threads created by
+      control the :class:`threading.Thread` names for worker threads created by
       the pool for easier debugging.
 
    .. versionchanged:: 3.7
index 0bd03b6..3217702 100644 (file)
@@ -295,6 +295,8 @@ On top of the core functionality, :class:`ConfigParser` supports
 interpolation.  This means values can be preprocessed before returning them
 from ``get()`` calls.
 
+.. index:: single: % (percent); interpolation in configuration files
+
 .. class:: BasicInterpolation()
 
    The default implementation used by :class:`ConfigParser`.  It enables
@@ -323,6 +325,8 @@ from ``get()`` calls.
    ``%(my_dir)s/Pictures`` as the value of ``my_pictures`` and
    ``%(home_dir)s/lumberjack`` as the value of ``my_dir``.
 
+.. index:: single: $ (dollar); interpolation in configuration files
+
 .. class:: ExtendedInterpolation()
 
    An alternative handler for interpolation which implements a more advanced
@@ -397,11 +401,11 @@ However, there are a few differences that should be taken into account:
   because default values cannot be deleted from the section (because technically
   they are not there).  If they are overridden in the section, deleting causes
   the default value to be visible again.  Trying to delete a default value
-  causes a ``KeyError``.
+  causes a :exc:`KeyError`.
 
 * ``DEFAULTSECT`` cannot be removed from the parser:
 
-  * trying to delete it raises ``ValueError``,
+  * trying to delete it raises :exc:`ValueError`,
 
   * ``parser.clear()`` leaves it intact,
 
@@ -695,7 +699,7 @@ More advanced customization may be achieved by overriding default values of
 these parser attributes.  The defaults are defined on the classes, so they may
 be overridden by subclasses or by attribute assignment.
 
-.. attribute:: BOOLEAN_STATES
+.. attribute:: ConfigParser.BOOLEAN_STATES
 
   By default when using :meth:`~ConfigParser.getboolean`, config parsers
   consider the following values ``True``: ``'1'``, ``'yes'``, ``'true'``,
@@ -718,7 +722,7 @@ be overridden by subclasses or by attribute assignment.
   Other typical Boolean pairs include ``accept``/``reject`` or
   ``enabled``/``disabled``.
 
-.. method:: optionxform(option)
+.. method:: ConfigParser.optionxform(option)
 
   This method transforms option names on every read, get, or set
   operation.  The default converts the name to lowercase.  This also
@@ -749,7 +753,7 @@ be overridden by subclasses or by attribute assignment.
      >>> list(custom['Section2'].keys())
      ['AnotherKey']
 
-.. attribute:: SECTCRE
+.. attribute:: ConfigParser.SECTCRE
 
   A compiled regular expression used to parse section headers.  The default
   matches ``[section]`` to the name ``"section"``.  Whitespace is considered
index 78f1619..634ff00 100644 (file)
@@ -51,10 +51,11 @@ A small number of constants live in the built-in namespace.  They are:
       See :exc:`NotImplementedError` for details on when to use it.
 
 
+.. index:: single: ...; ellipsis literal
 .. data:: Ellipsis
 
-   The same as ``...``.  Special value used mostly in conjunction with extended
-   slicing syntax for user-defined container data types.
+   The same as the ellipsis literal "``...``".  Special value used mostly in conjunction
+   with extended slicing syntax for user-defined container data types.
 
 
 .. data:: __debug__
index 2041d91..c7bd89f 100644 (file)
@@ -22,7 +22,7 @@ Interface summary:
    Return a shallow copy of *x*.
 
 
-.. function:: deepcopy(x)
+.. function:: deepcopy(x[, memo])
 
    Return a deep copy of *x*.
 
@@ -52,7 +52,7 @@ copy operations:
 
 The :func:`deepcopy` function avoids these problems by:
 
-* keeping a "memo" dictionary of objects already copied during the current
+* keeping a ``memo`` dictionary of objects already copied during the current
   copying pass; and
 
 * letting user-defined classes override the copying operation or the set of
@@ -82,7 +82,7 @@ In order for a class to define its own copy implementation, it can define
 special methods :meth:`__copy__` and :meth:`__deepcopy__`.  The former is called
 to implement the shallow copy operation; no additional arguments are passed.
 The latter is called to implement the deep copy operation; it is passed one
-argument, the memo dictionary.  If the :meth:`__deepcopy__` implementation needs
+argument, the ``memo`` dictionary.  If the :meth:`__deepcopy__` implementation needs
 to make a deep copy of a component, it should call the :func:`deepcopy` function
 with the component as first argument and the memo dictionary as second argument.
 
index dd62cb3..43d4b5b 100644 (file)
@@ -91,7 +91,7 @@ The :mod:`crypt` module defines the following functions:
    may be available on all platforms), or a full encrypted password
    including salt, as returned by this function.  If *salt* is not
    provided, the strongest method will be used (as returned by
-   :func:`methods`.
+   :func:`methods`).
 
    Checking a password is usually done by passing the plain-text password
    as *word* and the full results of a previous :func:`crypt` call,
index 04b2a26..a69dbb2 100644 (file)
@@ -207,6 +207,10 @@ The following function takes either a single-character string or integer value;
 it returns a string.
 
 
+.. index::
+   single: ^ (caret); in curses module
+   single: ! (exclamation); in curses module
+
 .. function:: unctrl(c)
 
    Return a string representation of the ASCII character *c*.  If *c* is printable,
index 7a276b1..db3a652 100644 (file)
@@ -276,9 +276,10 @@ Supported operations:
 | ``+t1``                        | Returns a :class:`timedelta` object with the  |
 |                                | same value. (2)                               |
 +--------------------------------+-----------------------------------------------+
-| ``-t1``                        | equivalent to :class:`timedelta`\             |
-|                                | (-*t1.days*, -*t1.seconds*,                   |
-|                                | -*t1.microseconds*), and to *t1*\* -1. (1)(4) |
+| ``-t1``                        | equivalent to                                 |
+|                                | :class:`timedelta`\ (-*t1.days*,              |
+|                                | -*t1.seconds*, -*t1.microseconds*),           |
+|                                | and to *t1*\* -1. (1)(4)                      |
 +--------------------------------+-----------------------------------------------+
 | ``abs(t)``                     | equivalent to +\ *t* when ``t.days >= 0``, and|
 |                                | to -*t* when ``t.days < 0``. (2)              |
@@ -1999,6 +2000,9 @@ Class attributes:
    The UTC timezone, ``timezone(timedelta(0))``.
 
 
+.. index::
+   single: % (percent); datetime format
+
 .. _strftime-strptime-behavior:
 
 :meth:`strftime` and :meth:`strptime` Behavior
@@ -2013,7 +2017,9 @@ although not all objects support a :meth:`timetuple` method.
 Conversely, the :meth:`datetime.strptime` class method creates a
 :class:`.datetime` object from a string representing a date and time and a
 corresponding format string. ``datetime.strptime(date_string, format)`` is
-equivalent to ``datetime(*(time.strptime(date_string, format)[0:6]))``.
+equivalent to ``datetime(*(time.strptime(date_string, format)[0:6]))``, except
+when the format includes sub-second components or timezone offset information,
+which are supported in ``datetime.strptime`` but are discarded by ``time.strptime``.
 
 For :class:`.time` objects, the format codes for year, month, and day should not
 be used, as time objects have no such values.  If they're used anyway, ``1900``
index 6743bdc..f044cb2 100644 (file)
@@ -457,14 +457,15 @@ The :class:`SequenceMatcher` class has this constructor:
 
    .. method:: get_matching_blocks()
 
-      Return list of triples describing matching subsequences. Each triple is of
-      the form ``(i, j, n)``, and means that ``a[i:i+n] == b[j:j+n]``.  The
+      Return list of triples describing non-overlapping matching subsequences.
+      Each triple is of the form ``(i, j, n)``,
+      and means that ``a[i:i+n] == b[j:j+n]``.  The
       triples are monotonically increasing in *i* and *j*.
 
       The last triple is a dummy, and has the value ``(len(a), len(b), 0)``.  It
       is the only triple with ``n == 0``.  If ``(i, j, n)`` and ``(i', j', n')``
       are adjacent triples in the list, and the second is not the last triple in
-      the list, then ``i+n != i'`` or ``j+n != j'``; in other words, adjacent
+      the list, then ``i+n < i'`` or ``j+n < j'``; in other words, adjacent
       triples always describe non-adjacent equal blocks.
 
       .. XXX Explain why a dummy is used!
index 587a0a0..a138e68 100644 (file)
@@ -321,6 +321,10 @@ but doctest isn't trying to do an exact emulation of any specific Python shell.
    NO!!!
    >>>
 
+.. index::
+   single: >>>; interpreter prompt
+   single: ...; interpreter prompt
+
 Any expected output must immediately follow the final ``'>>> '`` or ``'... '``
 line containing the code, and the expected output (if any) extends to the next
 ``'>>> '`` or all-whitespace line.
@@ -481,6 +485,8 @@ Some details you should read once, but won't need to remember:
   to test a :exc:`SyntaxError` that omits the traceback header, you will need to
   manually add the traceback header line to your test example.
 
+.. index:: single: ^ (caret); marker
+
 * For some :exc:`SyntaxError`\ s, Python displays the character position of the
   syntax error, using a ``^`` marker::
 
@@ -532,6 +538,7 @@ doctest decides whether actual output matches an example's expected output:
    option will probably go away, but not for several years.
 
 
+.. index:: single: <BLANKLINE>
 .. data:: DONT_ACCEPT_BLANKLINE
 
    By default, if an expected output block contains a line containing only the
@@ -551,6 +558,7 @@ doctest decides whether actual output matches an example's expected output:
    your source.
 
 
+.. index:: single: ...; in doctests
 .. data:: ELLIPSIS
 
    When specified, an ellipsis marker (``...``) in the expected output can match
@@ -686,6 +694,10 @@ useful unless you intend to extend :mod:`doctest` internals via subclassing:
       MY_FLAG = register_optionflag('MY_FLAG')
 
 
+.. index::
+   single: # (hash); in doctests
+   single: + (plus); in doctests
+   single: - (minus); in doctests
 .. _doctest-directives:
 
 Directives
index 2e189dc..f02b35b 100644 (file)
@@ -149,10 +149,10 @@ Here are the methods of the :class:`Message` class:
 
    .. method:: is_multipart()
 
-      Return ``True`` if the message's payload is a list of sub-\
-      :class:`Message` objects, otherwise return ``False``.  When
+      Return ``True`` if the message's payload is a list of
+      sub-\ :class:`Message` objects, otherwise return ``False``.  When
       :meth:`is_multipart` returns ``False``, the payload should be a string
-      object (which might be a CTE encoded binary payload.  (Note that
+      object (which might be a CTE encoded binary payload).  (Note that
       :meth:`is_multipart` returning ``True`` does not necessarily mean that
       "msg.get_content_maintype() == 'multipart'" will return the ``True``.
       For example, ``is_multipart`` will return ``True`` when the
index 261d0d6..77b8099 100644 (file)
@@ -92,7 +92,7 @@ message objects.
 
    .. method:: __str__()
 
-      Equivalent to `as_string(policy=self.policy.clone(utf8=True)`.  Allows
+      Equivalent to ``as_string(policy=self.policy.clone(utf8=True))``.  Allows
       ``str(msg)`` to produce a string containing the serialized message in a
       readable format.
 
@@ -130,8 +130,8 @@ message objects.
 
    .. method:: is_multipart()
 
-      Return ``True`` if the message's payload is a list of sub-\
-      :class:`EmailMessage` objects, otherwise return ``False``.  When
+      Return ``True`` if the message's payload is a list of
+      sub-\ :class:`EmailMessage` objects, otherwise return ``False``.  When
       :meth:`is_multipart` returns ``False``, the payload should be a string
       object (which might be a CTE encoded binary payload).  Note that
       :meth:`is_multipart` returning ``True`` does not necessarily mean that
@@ -379,7 +379,7 @@ message objects.
 
       Note that existing parameter values of headers may be accessed through
       the :attr:`~email.headerregistry.BaseHeader.params` attribute of the
-      header value (for example, ``msg['Content-Type'].params['charset']``.
+      header value (for example, ``msg['Content-Type'].params['charset']``).
 
       .. versionchanged:: 3.4 ``replace`` keyword was added.
 
@@ -679,7 +679,7 @@ message objects.
       specified by the current :mod:`~email.policy`.  If the added part
       has no :mailheader:`Content-Disposition` header, add one with the value
       ``attachment``.  This method can be used both for explicit attachments
-      (:mailheader:`Content-Disposition: attachment` and ``inline`` attachments
+      (:mailheader:`Content-Disposition: attachment`) and ``inline`` attachments
       (:mailheader:`Content-Disposition: inline`), by passing appropriate
       options to the ``content_manager``.
 
index e0cab6a..d9a6161 100644 (file)
@@ -164,7 +164,7 @@ message body, instead setting the payload to the raw body.
       envelope header.  The header block is terminated either by the end of the
       data or by a blank line.  Following the header block is the body of the
       message (which may contain MIME-encoded subparts, including subparts
-      with a :mailheader:`Content-Transfer-Encoding` of ``8bit``.
+      with a :mailheader:`Content-Transfer-Encoding` of ``8bit``).
 
       Optional *headersonly* is a flag specifying whether to stop parsing after
       reading the headers or not.  The default is ``False``, meaning it parses
@@ -238,7 +238,7 @@ in the top-level :mod:`email` package namespace.
 
    Return a message object structure from a :term:`bytes-like object`.  This is
    equivalent to ``BytesParser().parsebytes(s)``.  Optional *_class* and
-   *strict* are interpreted as with the :class:`~email.parser.BytesParser` class
+   *policy* are interpreted as with the :class:`~email.parser.BytesParser` class
    constructor.
 
    .. versionadded:: 3.2
index 1033d8c..fae99cf 100644 (file)
@@ -87,7 +87,7 @@ to advanced applications.
 Following those is a set of examples of using the fundamental parts of the APIs
 covered in the preceding sections.
 
-The forgoing represent the modern (unicode friendly) API of the email package.
+The foregoing represent the modern (unicode friendly) API of the email package.
 The remaining sections, starting with the :class:`~email.message.Message`
 class, cover the legacy :data:`~email.policy.compat32` API that deals much more
 directly with the details of how email messages are represented.  The
@@ -126,7 +126,7 @@ Legacy API:
    email.header.rst
    email.charset.rst
    email.encoders.rst
-   email.util.rst
+   email.utils.rst
    email.iterators.rst
 
 
index e33b886..57ed291 100644 (file)
@@ -52,7 +52,7 @@ will be set as :attr:`__cause__` on the raised exception. Setting
 :attr:`__cause__` also implicitly sets the :attr:`__suppress_context__`
 attribute to ``True``, so that using ``raise new_exc from None``
 effectively replaces the old exception with the new one for display
-purposes (e.g. converting :exc:`KeyError` to :exc:`AttributeError`, while
+purposes (e.g. converting :exc:`KeyError` to :exc:`AttributeError`), while
 leaving the old exception available in :attr:`__context__` for introspection
 when debugging.
 
index abf9523..ce07d32 100644 (file)
@@ -16,6 +16,13 @@ This module provides support for Unix shell-style wildcards, which are *not* the
 same as regular expressions (which are documented in the :mod:`re` module).  The
 special characters used in shell-style wildcards are:
 
+.. index::
+   single: * (asterisk); in glob-style wildcards
+   single: ? (question mark); in glob-style wildcards
+   single: [] (square brackets); in glob-style wildcards
+   single: ! (exclamation); in glob-style wildcards
+   single: - (minus); in glob-style wildcards
+
 +------------+------------------------------------+
 | Pattern    | Meaning                            |
 +============+====================================+
@@ -35,7 +42,7 @@ For example, ``'[?]'`` matches the character ``'?'``.
 
 Note that the filename separator (``'/'`` on Unix) is *not* special to this
 module.  See module :mod:`glob` for pathname expansion (:mod:`glob` uses
-:func:`fnmatch` to match pathname segments).  Similarly, filenames starting with
+:func:`.filter` to match pathname segments).  Similarly, filenames starting with
 a period are not special for this module, and are matched by the ``*`` and ``?``
 patterns.
 
index bb47286..c0f4ffd 100644 (file)
@@ -1405,8 +1405,8 @@ are always available.  They are listed here in alphabetical order.
    Has two optional arguments which must be specified as keyword arguments.
 
    *key* specifies a function of one argument that is used to extract a comparison
-   key from each list element: ``key=str.lower``.  The default value is ``None``
-   (compare the elements directly).
+   key from each element in *iterable* (for example, ``key=str.lower``).  The
+   default value is ``None`` (compare the elements directly).
 
    *reverse* is a boolean value.  If set to ``True``, then the list elements are
    sorted as if each comparison were reversed.
index 3413cd3..d19373b 100644 (file)
@@ -85,6 +85,11 @@ The :mod:`functools` module defines the following functions:
    The cache's size limit assures that the cache does not grow without bound on
    long-running processes such as web servers.
 
+   In general, the LRU cache should only be used when you want to reuse
+   previously computed values.  Accordingly, it doesn't make sense to cache
+   functions with side-effects, functions that need to create distinct mutable
+   objects on each call, or impure functions such as time() or random().
+
    Example of an LRU cache for static web content::
 
         @lru_cache(maxsize=32)
@@ -172,10 +177,11 @@ The :mod:`functools` module defines the following functions:
 
 .. function:: partial(func, *args, **keywords)
 
-   Return a new :class:`partial` object which when called will behave like *func*
-   called with the positional arguments *args* and keyword arguments *keywords*. If
-   more arguments are supplied to the call, they are appended to *args*. If
-   additional keyword arguments are supplied, they extend and override *keywords*.
+   Return a new :ref:`partial object<partial-objects>` which when called
+   will behave like *func* called with the positional arguments *args*
+   and keyword arguments *keywords*. If more arguments are supplied to the
+   call, they are appended to *args*. If additional keyword arguments are
+   supplied, they extend and override *keywords*.
    Roughly equivalent to::
 
       def partial(func, *args, **keywords):
@@ -214,7 +220,7 @@ The :mod:`functools` module defines the following functions:
    :func:`classmethod`, :func:`staticmethod`, :func:`abstractmethod` or
    another instance of :class:`partialmethod`), calls to ``__get__`` are
    delegated to the underlying descriptor, and an appropriate
-   :class:`partial` object returned as the result.
+   :ref:`partial object<partial-objects>` returned as the result.
 
    When *func* is a non-descriptor callable, an appropriate bound method is
    created dynamically. This behaves like a normal Python function when
index 407853c..c98c5e7 100644 (file)
@@ -61,6 +61,7 @@ class-based API instead.
    *domain*, which is returned.
 
 
+.. index:: single: _ (underscore); gettext
 .. function:: gettext(message)
 
    Return the localized translation of *message*, based on the current global
index 25bd4b7..0db10b5 100644 (file)
 
 --------------
 
+.. index::
+   single: * (asterisk); in glob-style wildcards
+   single: ? (question mark); in glob-style wildcards
+   single: [] (square brackets); in glob-style wildcards
+   single: ! (exclamation); in glob-style wildcards
+   single: - (minus); in glob-style wildcards
+   single: . (dot); in glob-style wildcards
+
 The :mod:`glob` module finds all the pathnames matching a specified pattern
 according to the rules used by the Unix shell, although results are returned in
 arbitrary order.  No tilde expansion is done, but ``*``, ``?``, and character
@@ -36,6 +44,9 @@ For example, ``'[?]'`` matches the character ``'?'``.
    :file:`../../Tools/\*/\*.gif`), and can contain shell-style wildcards. Broken
    symlinks are included in the results (as in the shell).
 
+   .. index::
+      single: **; in glob-style wildcards
+
    If *recursive* is true, the pattern "``**``" will match any files and zero or
    more directories and subdirectories.  If the pattern is followed by an
    ``os.sep``, only directories and subdirectories match.
index baf6b0a..ab4414f 100644 (file)
@@ -271,7 +271,7 @@ include a `salt <https://en.wikipedia.org/wiki/Salt_%28cryptography%29>`_.
    factor and *maxmem* limits memory (OpenSSL 1.1.0 defaults to 32 MiB).
    *dklen* is the length of the derived key.
 
-   Availability: OpenSSL 1.1+
+   .. availability:: OpenSSL 1.1+.
 
    .. versionadded:: 3.6
 
index 0b1a3c8..8b00f7b 100644 (file)
@@ -112,17 +112,17 @@ The module also offers three general purpose functions based on heaps.
 
    Return a list with the *n* largest elements from the dataset defined by
    *iterable*.  *key*, if provided, specifies a function of one argument that is
-   used to extract a comparison key from each element in the iterable:
-   ``key=str.lower`` Equivalent to:  ``sorted(iterable, key=key,
-   reverse=True)[:n]``
+   used to extract a comparison key from each element in *iterable* (for example,
+   ``key=str.lower``).  Equivalent to:  ``sorted(iterable, key=key,
+   reverse=True)[:n]``.
 
 
 .. function:: nsmallest(n, iterable, key=None)
 
    Return a list with the *n* smallest elements from the dataset defined by
    *iterable*.  *key*, if provided, specifies a function of one argument that is
-   used to extract a comparison key from each element in the iterable:
-   ``key=str.lower`` Equivalent to:  ``sorted(iterable, key=key)[:n]``
+   used to extract a comparison key from each element in *iterable* (for example,
+   ``key=str.lower``).  Equivalent to:  ``sorted(iterable, key=key)[:n]``.
 
 
 The latter two functions perform best for smaller values of *n*.  For larger
index 0bd7f77..7e317cd 100644 (file)
 
 This module defines classes for implementing HTTP servers (Web servers).
 
+.. warning::
+
+    :mod:`http.server` is not recommended for production. It only implements
+    basic security checks.
+
 One class, :class:`HTTPServer`, is a :class:`socketserver.TCPServer` subclass.
 It creates and listens at the HTTP socket, dispatching the requests to a
 handler.  Code to create and run the server looks like this::
index 0eb1b44..384d2bf 100644 (file)
@@ -20,7 +20,7 @@ IDLE has the following features:
 
 * coded in 100% pure Python, using the :mod:`tkinter` GUI toolkit
 
-* cross-platform: works mostly the same on Windows, Unix, and Mac OS X
+* cross-platform: works mostly the same on Windows, Unix, and macOS
 
 * Python shell window (interactive interpreter) with colorizing
   of code input, output, and error messages
@@ -40,13 +40,17 @@ Menus
 -----
 
 IDLE has two main window types, the Shell window and the Editor window.  It is
-possible to have multiple editor windows simultaneously.  Output windows, such
-as used for Edit / Find in Files, are a subtype of edit window.  They currently
-have the same top menu as Editor windows but a different default title and
-context menu.
+possible to have multiple editor windows simultaneously.  On Windows and
+Linux, each has its own top menu.  Each menu documented below indicates
+which window type it is associated with.
 
-IDLE's menus dynamically change based on which window is currently selected.
-Each menu documented below indicates which window type it is associated with.
+Output windows, such as used for Edit => Find in Files, are a subtype of editor
+window.  They currently have the same top menu but a different
+default title and context menu.
+
+On macOS, there is one application menu.  It dynamically changes according
+to the window currently selected.  It has an IDLE menu, and some entries
+described below are moved around to conform to Apple guidlines.
 
 File menu (Shell and Editor)
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -263,7 +267,7 @@ Options menu (Shell and Editor)
 Configure IDLE
    Open a configuration dialog and change preferences for the following:
    fonts, indentation, keybindings, text color themes, startup windows and
-   size, additional help sources, and extensions (see below).  On OS X,
+   size, additional help sources, and extensions (see below).  On macOS,
    open the configuration dialog by selecting Preferences in the application
    menu.  To use a new built-in color theme (IDLE Dark) with older IDLEs,
    save it as a new custom theme.
@@ -295,7 +299,7 @@ About IDLE
    Display version, copyright, license, credits, and more.
 
 IDLE Help
-   Display a help file for IDLE detailing the menu options, basic editing and
+   Display this IDLE document, detailing the menu options, basic editing and
    navigation, and other tips.
 
 Python Docs
@@ -306,7 +310,8 @@ Turtle Demo
    Run the turtledemo module with example Python code and turtle drawings.
 
 Additional help sources may be added here with the Configure IDLE dialog under
-the General tab.
+the General tab. See the "Help sources" subsection below for more
+on Help menu choices.
 
 .. index::
    single: Cut
@@ -319,7 +324,7 @@ the General tab.
 Context Menus
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-Open a context menu by right-clicking in a window (Control-click on OS X).
+Open a context menu by right-clicking in a window (Control-click on macOS).
 Context menus have the standard clipboard functions also on the Edit menu.
 
 Cut
@@ -341,17 +346,42 @@ Set Breakpoint
 Clear Breakpoint
    Clear the breakpoint on that line.
 
-Shell and Output windows have the following.
+Shell and Output windows also have the following.
 
 Go to file/line
    Same as in Debug menu.
 
+The Shell window also has an output squeezing facility explained in the
+the *Python Shell window* subsection below.
+
+Squeeze
+   If the cursor is over an output line, squeeze all the output between
+   the code above and the prompt below down to a 'Squeezed text' label.
+
 
 Editing and navigation
 ----------------------
 
+Editor windows
+^^^^^^^^^^^^^^
+
+IDLE may open editor windows when it starts, depending on settings
+and how you start IDLE.  Thereafter, use the File menu.  There can be only
+one open editor window for a given file.
+
+The title bar contains the name of the file, the full path, and the version
+of Python and IDLE running the window.  The status bar contains the line
+number ('Ln') and column number ('Col').  Line numbers start with 1;
+column numbers with 0.
+
+IDLE assumes that files with a known .py* extension contain Python code
+and that other files do not.  Run Python code with the Run menu.
+
+Key bindings
+^^^^^^^^^^^^
+
 In this section, 'C' refers to the :kbd:`Control` key on Windows and Unix and
-the :kbd:`Command` key on Mac OSX.
+the :kbd:`Command` key on macOS.
 
 * :kbd:`Backspace` deletes to the left; :kbd:`Del` deletes to the right
 
@@ -389,7 +419,6 @@ the :kbd:`Command` key on Mac OSX.
 Standard keybindings (like :kbd:`C-c` to copy and :kbd:`C-v` to paste)
 may work.  Keybindings are selected in the Configure IDLE dialog.
 
-
 Automatic indentation
 ^^^^^^^^^^^^^^^^^^^^^
 
@@ -477,6 +506,17 @@ or immediately run an existing file before editing.
 Python Shell window
 ^^^^^^^^^^^^^^^^^^^
 
+With IDLE's Shell, one enters, edits, and recalls complete statements.
+Most consoles and terminals only work with a single physical line at a time.
+
+When one pastes code into Shell, it is not compiled and possibly executed
+until one hits :kbd:`Return`.  One may edit pasted code first.
+If one pastes more that one statement into Shell, the result will be a
+:exc:`SyntaxError` when multiple statements are compiled as if they were one.
+
+The editing features described in previous subsections work when entering
+code interactively.  IDLE's Shell window also responds to the following keys.
+
 * :kbd:`C-c` interrupts executing command
 
 * :kbd:`C-d` sends end-of-file; closes window if typed at a ``>>>`` prompt
@@ -486,13 +526,12 @@ Python Shell window
   Command history
 
   * :kbd:`Alt-p` retrieves previous command matching what you have typed. On
-    OS X use :kbd:`C-p`.
+    macOS use :kbd:`C-p`.
 
-  * :kbd:`Alt-n` retrieves next. On OS X use :kbd:`C-n`.
+  * :kbd:`Alt-n` retrieves next. On macOS use :kbd:`C-n`.
 
   * :kbd:`Return` while on any previous command retrieves that command
 
-
 Text colors
 ^^^^^^^^^^^
 
@@ -526,7 +565,6 @@ looked for in the user's home directory.  Statements in this file will be
 executed in the Tk namespace, so this file is not useful for importing
 functions to be used from IDLE's Python shell.
 
-
 Command line usage
 ^^^^^^^^^^^^^^^^^^
 
@@ -554,7 +592,6 @@ If there are arguments:
 * Otherwise, arguments are files opened for editing and
   ``sys.argv`` reflects the arguments passed to IDLE itself.
 
-
 Startup failure
 ^^^^^^^^^^^^^^^
 
@@ -598,27 +635,84 @@ be to delete one or more of the configuration files.
 If IDLE quits with no message, and it was not started from a console, try
 starting from a console (``python -m idlelib)`` and see if a message appears.
 
-
-IDLE-console differences
-^^^^^^^^^^^^^^^^^^^^^^^^
+Running user code
+^^^^^^^^^^^^^^^^^
 
 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.
+intended to be the same as executing the same code by the default method,
+directly with Python in a text-mode system console or terminal window.
 However, the different interface and operation occasionally affect
-visible results.  For instance, ``sys.modules`` starts with more entries.
+visible results.  For instance, ``sys.modules`` starts with more entries,
+and ``threading.activeCount()`` returns 2 instead of 1.
+
+By default, IDLE runs user code in a separate OS process rather than in
+the user interface process that runs the shell and editor.  In the execution
+process, it replaces ``sys.stdin``, ``sys.stdout``, and ``sys.stderr``
+with objects that get input from and send output to the Shell window.
+The original values stored in ``sys.__stdin__``, ``sys.__stdout__``, and
+``sys.__stderr__`` are not touched, but may be ``None``.
 
-IDLE also replaces ``sys.stdin``, ``sys.stdout``, and ``sys.stderr`` with
-objects that get input from and send output to the Shell window.
 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.
-
-With IDLE's Shell, one enters, edits, and recalls complete statements.
-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.
+and screen will not work.  These include system-specific functions that
+determine whether a key has been pressed and if so, which.
+
+IDLE's standard stream replacements are not inherited by subprocesses
+created in the execution process, whether directly by user code or by modules
+such as multiprocessing.  If such subprocess use ``input`` from sys.stdin
+or ``print`` or ``write`` to sys.stdout or sys.stderr,
+IDLE should be started in a command line window.  The secondary subprocess
+will then be attached to that window for input and output.
+
+If ``sys`` is reset by user code, such as with ``importlib.reload(sys)``,
+IDLE's changes are lost and input from the keyboard and output to the screen
+will not work correctly.
+
+User output in Shell
+^^^^^^^^^^^^^^^^^^^^
+
+When a program outputs text, the result is determined by the
+corresponding output device.  When IDLE executes user code, ``sys.stdout``
+and ``sys.stderr`` are connected to the display area of IDLE's Shell.  Some of
+its features are inherited from the underlying Tk Text widget.  Others
+are programmed additions.  Where it matters, Shell is designed for development
+rather than production runs.
+
+For instance, Shell never throws away output.  A program that sends unlimited
+output to Shell will eventually fill memory, resulting in a memory error.
+In contrast, some system text windows only keep the last n lines of output.
+A Windows console, for instance, keeps a user-settable 1 to 9999 lines,
+with 300 the default.
+
+Text widgets display a subset of Unicode, the Basic Multilingual Plane (BMP).
+Which characters get a proper glyph instead of a replacement box depends on
+the operating system and installed fonts.  Newline characters cause following
+text to appear on a new line, but other control characters are either
+replaced with a box or deleted.  However, ``repr()``, which is used for
+interactive echo of expression values, replaces control characters,
+some BMP codepoints, and all non-BMP characters with escape codes
+before they are output.
+
+Normal and error output are generally kept separate (on separate lines)
+from code input and each other.  They each get different highlight colors.
+
+For SyntaxError tracebacks, the normal '^' marking where the error was
+detected is replaced by coloring the text with an error highlight.
+When code run from a file causes other exceptions, one may right click
+on a traceback line to jump to the corresponding line in an IDLE editor.
+The file will be opened if necessary.
+
+Shell has a special facility for squeezing output lines down to a
+'Squeezed text' label.  This is done automatically
+for output over N lines (N = 50 by default).
+N can be changed in the PyShell section of the General
+page of the Settings dialog.  Output with fewer lines can be squeezed by
+right clicking on the output.  This can be useful lines long enough to slow
+down scrolling.
+
+Squeezed output is expanded in place by double-clicking the label.
+It can also be sent to the clipboard or a separate view window by
+right-clicking the label.
 
 Developing tkinter applications
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -674,15 +768,25 @@ with the default subprocess if at all possible.
 Help and preferences
 --------------------
 
-Additional help sources
-^^^^^^^^^^^^^^^^^^^^^^^
+Help sources
+^^^^^^^^^^^^
 
-IDLE includes a help menu entry called "Python Docs" that will open the
-extensive sources of help, including tutorials, available at docs.python.org.
-Selected URLs can be added or removed from the help menu at any time using the
-Configure IDLE dialog. See the IDLE help option in the help menu of IDLE for
-more information.
+Help menu entry "IDLE Help" displays a formatted html version of the
+IDLE chapter of the Library Reference.  The result, in a read-only
+tkinter text window, is close to what one sees in a web browser.
+Navigate through the text with a mousewheel,
+the scrollbar, or up and down arrow keys held down.
+Or click the TOC (Table of Contents) button and select a section
+header in the opened box.
 
+Help menu entry "Python Docs" opens the extensive sources of help,
+including tutorials, available at docs.python.org/x.y, where 'x.y'
+is the currently running Python version.  If your system
+has an off-line copy of the docs (this may be an installation option),
+that will be opened instead.
+
+Selected URLs can be added or removed from the help menu at any time using the
+General tab of the Configure IDLE dialog .
 
 Setting preferences
 ^^^^^^^^^^^^^^^^^^^
@@ -692,6 +796,12 @@ changed via Configure IDLE on the Option menu.  Keys can be user defined;
 IDLE ships with four built-in key sets. In addition, a user can create a
 custom key set in the Configure IDLE dialog under the keys tab.
 
+IDLE on macOS
+^^^^^^^^^^^^^
+
+Under System Preferences: Dock, one can set "Prefer tabs when opening
+documents" to "Always".  This setting is not compatible with the tk/tkinter
+GUI framework used by IDLE, and it breaks a few IDLE features.
 
 Extensions
 ^^^^^^^^^^
index ccf5f92..1bd6f12 100644 (file)
@@ -229,7 +229,7 @@ file paths.
    file path.  For example, if *path* is
    ``/foo/bar/__pycache__/baz.cpython-32.pyc`` the returned path would be
    ``/foo/bar/baz.py``.  *path* need not exist, however if it does not conform
-   to :pep:`3147` format, a ``ValueError`` is raised. If
+   to :pep:`3147` format, a :exc:`ValueError` is raised. If
    :attr:`sys.implementation.cache_tag` is not defined,
    :exc:`NotImplementedError` is raised.
 
index 6f4da11..0bcfbb1 100644 (file)
@@ -1407,7 +1407,7 @@ an :term:`importer`.
    file path.  For example, if *path* is
    ``/foo/bar/__pycache__/baz.cpython-32.pyc`` the returned path would be
    ``/foo/bar/baz.py``.  *path* need not exist, however if it does not conform
-   to :pep:`3147` or :pep:`488` format, a ``ValueError`` is raised. If
+   to :pep:`3147` or :pep:`488` format, a :exc:`ValueError` is raised. If
    :attr:`sys.implementation.cache_tag` is not defined,
    :exc:`NotImplementedError` is raised.
 
@@ -1653,7 +1653,7 @@ Importing a source file directly
 ''''''''''''''''''''''''''''''''
 
 To import a Python source file directly, use the following recipe
-(Python 3.4 and newer only)::
+(Python 3.5 and newer only)::
 
   import importlib.util
   import sys
@@ -1731,7 +1731,7 @@ Python 3.6 and newer for other parts of the code).
       if '.' in absolute_name:
           parent_name, _, child_name = absolute_name.rpartition('.')
           parent_module = import_module(parent_name)
-          path = parent_module.spec.submodule_search_locations
+          path = parent_module.__spec__.submodule_search_locations
       for finder in sys.meta_path:
           spec = finder.find_spec(absolute_name, path)
           if spec is not None:
index 300b3e7..f985eeb 100644 (file)
@@ -132,9 +132,6 @@ attributes:
 |           | f_locals          | local namespace seen by   |
 |           |                   | this frame                |
 +-----------+-------------------+---------------------------+
-|           | f_restricted      | 0 or 1 if frame is in     |
-|           |                   | restricted execution mode |
-+-----------+-------------------+---------------------------+
 |           | f_trace           | tracing function for this |
 |           |                   | frame, or ``None``        |
 +-----------+-------------------+---------------------------+
index e3283ca..8567e4d 100644 (file)
@@ -47,3 +47,16 @@ this material.
 
 Let the show begin!
 
+
+.. _availability:
+
+Notes on availability
+=====================
+
+* An "Availability: Unix" note means that this function is commonly found on
+  Unix systems.  It does not make any claims about its existence on a specific
+  operating system.
+
+* If not separately noted, all functions that claim "Availability: Unix" are
+  supported on Mac OS X, which builds on a Unix core.
+
index 98649de..7068e68 100644 (file)
@@ -31,7 +31,7 @@ be used for each of them.  A concrete object belonging to any of these
 categories is called a :term:`file object`.  Other common terms are *stream*
 and *file-like object*.
 
-Independently of its category, each concrete stream object will also have
+Independent of its category, each concrete stream object will also have
 various capabilities: it can be read-only, write-only, or read-write. It can
 also allow arbitrary random access (seeking forwards or backwards to any
 location), or only sequential access (for example in the case of a socket or
@@ -39,7 +39,7 @@ pipe).
 
 All streams are careful about the type of data you give to them.  For example
 giving a :class:`str` object to the ``write()`` method of a binary stream
-will raise a ``TypeError``.  So will giving a :class:`bytes` object to the
+will raise a :exc:`TypeError`.  So will giving a :class:`bytes` object to the
 ``write()`` method of a text stream.
 
 .. versionchanged:: 3.3
index 8ce493d..510e307 100644 (file)
@@ -648,7 +648,9 @@ when serializing Python :class:`int` values of extremely large magnitude, or
 when serializing instances of "exotic" numerical types such as
 :class:`decimal.Decimal`.
 
+
 .. _json-commandline:
+.. program:: json.tool
 
 Command Line Interface
 ----------------------
@@ -680,6 +682,7 @@ specified, :attr:`sys.stdin` and :attr:`sys.stdout` will be used respectively:
    :option:`--sort-keys` option to sort the output of dictionaries
    alphabetically by key.
 
+
 Command line options
 ^^^^^^^^^^^^^^^^^^^^
 
index 2fd44fe..bf57a08 100644 (file)
@@ -148,10 +148,8 @@ The :mod:`locale` module defines the following exception and functions:
    +--------------+-----------------------------------------+
 
    The function sets temporarily the ``LC_CTYPE`` locale to the ``LC_NUMERIC``
-   locale to decode ``decimal_point`` and ``thousands_sep`` byte strings if
-   they are non-ASCII or longer than 1 byte, and the ``LC_NUMERIC`` locale is
-   different than the ``LC_CTYPE`` locale. This temporary change affects other
-   threads.
+   locale or the ``LC_MONETARY`` locale if locales are different and numeric or
+   monetary strings are non-ASCII. This temporary change affects other threads.
 
    .. versionchanged:: 3.7
       The function now sets temporarily the ``LC_CTYPE`` locale to the
index a2188c8..0e65f31 100644 (file)
@@ -882,7 +882,7 @@ re-entrant, and so cannot be invoked from such signal handlers.
 Module-Level Functions
 ----------------------
 
-In addition to the classes described above, there are a number of module- level
+In addition to the classes described above, there are a number of module-level
 functions.
 
 
@@ -931,8 +931,8 @@ functions.
    There are three keyword arguments in *kwargs* which are inspected: *exc_info*
    which, if it does not evaluate as false, causes exception information to be
    added to the logging message. If an exception tuple (in the format returned by
-   :func:`sys.exc_info`) is provided, it is used; otherwise, :func:`sys.exc_info`
-   is called to get the exception information.
+   :func:`sys.exc_info`) or an exception instance is provided, it is used;
+   otherwise, :func:`sys.exc_info` is called to get the exception information.
 
    The second optional keyword argument is *stack_info*, which defaults to
    ``False``. If true, stack information is added to the logging
index 67b7a71..5c1982b 100644 (file)
@@ -255,7 +255,9 @@ than one MIME-type database; it provides an interface similar to the one of the
 
    .. method:: MimeTypes.read_windows_registry(strict=True)
 
-      Load MIME type information from the Windows registry.  Availability: Windows.
+      Load MIME type information from the Windows registry.
+
+      .. availability:: Windows.
 
       If *strict* is ``True``, information will be added to the list of standard
       types, else to the list of non-standard types.
index ca09a6a..f0d2071 100644 (file)
@@ -65,7 +65,7 @@ To map anonymous memory, -1 should be passed as the fileno along with the length
 
    *offset* may be specified as a non-negative integer offset. mmap references
    will be relative to the offset from the beginning of the file. *offset*
-   defaults to 0.  *offset* must be a multiple of the ALLOCATIONGRANULARITY.
+   defaults to 0.  *offset* must be a multiple of the :const:`ALLOCATIONGRANULARITY`.
 
 
 .. class:: mmap(fileno, length, flags=MAP_SHARED, prot=PROT_WRITE|PROT_READ, access=ACCESS_DEFAULT[, offset])
@@ -94,8 +94,8 @@ To map anonymous memory, -1 should be passed as the fileno along with the length
 
    *offset* may be specified as a non-negative integer offset. mmap references
    will be relative to the offset from the beginning of the file. *offset*
-   defaults to 0.  *offset* must be a multiple of the PAGESIZE or
-   ALLOCATIONGRANULARITY.
+   defaults to 0. *offset* must be a multiple of :const:`ALLOCATIONGRANULARITY`
+   which is equal to :const:`PAGESIZE` on Unix systems.
 
    To ensure validity of the created memory mapping the file specified
    by the descriptor *fileno* is internally automatically synchronized
@@ -189,7 +189,8 @@ To map anonymous memory, -1 should be passed as the fileno along with the length
       use of this call there is no guarantee that changes are written back before
       the object is destroyed.  If *offset* and *size* are specified, only
       changes to the given range of bytes will be flushed to disk; otherwise, the
-      whole extent of the mapping is flushed.
+      whole extent of the mapping is flushed.  *offset* must be a multiple of the
+      :const:`PAGESIZE` or :const:`ALLOCATIONGRANULARITY`.
 
       **(Windows version)** A nonzero value returned indicates success; zero
       indicates failure.
index e9b82ee..3afc77b 100644 (file)
@@ -379,8 +379,8 @@ types is covered in section :ref:`optparse-extending-optparse`.
 Handling boolean (flag) options
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-Flag options---set a variable to true or false when a particular option is seen
----are quite common.  :mod:`optparse` supports them with two separate actions,
+Flag options---set a variable to true or false when a particular option is
+seen---are quite common.  :mod:`optparse` supports them with two separate actions,
 ``store_true`` and ``store_false``.  For example, you might have a ``verbose``
 flag that is turned on with ``-v`` and off with ``-q``::
 
@@ -388,8 +388,8 @@ flag that is turned on with ``-v`` and off with ``-q``::
    parser.add_option("-q", action="store_false", dest="verbose")
 
 Here we have two different options with the same destination, which is perfectly
-OK.  (It just means you have to be a bit careful when setting default values---
-see below.)
+OK.  (It just means you have to be a bit careful when setting default
+values---see below.)
 
 When :mod:`optparse` encounters ``-v`` on the command line, it sets
 ``options.verbose`` to ``True``; when it encounters ``-q``,
@@ -525,9 +525,9 @@ help message:
   default: ``"Usage: %prog [options]"``, which is fine if your script doesn't
   take any positional arguments.
 
-* every option defines a help string, and doesn't worry about line-wrapping---
-  :mod:`optparse` takes care of wrapping lines and making the help output look
-  good.
+* every option defines a help string, and doesn't worry about
+  line-wrapping---\ :mod:`optparse` takes care of wrapping lines and making
+  the help output look good.
 
 * options that take a value indicate this fact in their automatically-generated
   help message, e.g. for the "mode" option::
index 36bb21c..f6ff010 100644 (file)
@@ -85,7 +85,7 @@ the :mod:`glob` module.)
    pathnames, or if *paths* is empty.  Unlike :func:`commonprefix`, this
    returns a valid path.
 
-   Availability: Unix, Windows
+   .. availability:: Unix, Windows.
 
    .. versionadded:: 3.5
 
@@ -152,6 +152,8 @@ the :mod:`glob` module.)
       Accepts a :term:`path-like object`.
 
 
+.. index:: single: ~ (tilde); home directory expansion
+
 .. function:: expanduser(path)
 
    On Unix and Windows, return the argument with an initial component of ``~`` or
@@ -175,6 +177,9 @@ the :mod:`glob` module.)
    .. versionchanged:: 3.6
       Accepts a :term:`path-like object`.
 
+.. index::
+   single: $ (dollar); environment variables expansion
+   single: % (percent); environment variables expansion (Windows)
 
 .. function:: expandvars(path)
 
@@ -312,7 +317,7 @@ the :mod:`glob` module.)
    Normalize the case of a pathname.  On Unix and Mac OS X, this returns the
    path unchanged; on case-insensitive filesystems, it converts the path to
    lowercase.  On Windows, it also converts forward slashes to backward slashes.
-   Raise a TypeError if the type of *path* is not ``str`` or ``bytes`` (directly
+   Raise a :exc:`TypeError` if the type of *path* is not ``str`` or ``bytes`` (directly
    or indirectly through the :class:`os.PathLike` interface).
 
    .. versionchanged:: 3.6
@@ -349,7 +354,7 @@ the :mod:`glob` module.)
 
    *start* defaults to :attr:`os.curdir`.
 
-   Availability: Unix, Windows.
+   .. availability:: Unix, Windows.
 
    .. versionchanged:: 3.6
       Accepts a :term:`path-like object`.
@@ -361,7 +366,7 @@ the :mod:`glob` module.)
    This is determined by the device number and i-node number and raises an
    exception if an :func:`os.stat` call on either pathname fails.
 
-   Availability: Unix, Windows.
+   .. availability:: Unix, Windows.
 
    .. versionchanged:: 3.2
       Added Windows support.
@@ -377,7 +382,7 @@ the :mod:`glob` module.)
 
    Return ``True`` if the file descriptors *fp1* and *fp2* refer to the same file.
 
-   Availability: Unix, Windows.
+   .. availability:: Unix, Windows.
 
    .. versionchanged:: 3.2
       Added Windows support.
@@ -393,7 +398,7 @@ the :mod:`glob` module.)
    :func:`os.lstat`, or :func:`os.stat`.  This function implements the
    underlying comparison used by :func:`samefile` and :func:`sameopenfile`.
 
-   Availability: Unix, Windows.
+   .. availability:: Unix, Windows.
 
    .. versionchanged:: 3.4
       Added Windows support.
index d937451..9ed999e 100644 (file)
@@ -32,15 +32,6 @@ Notes on the availability of these functions:
   objects, and result in an object of the same type, if a path or file name is
   returned.
 
-* An "Availability: Unix" note means that this function is commonly found on
-  Unix systems.  It does not make any claims about its existence on a specific
-  operating system.
-
-* If not separately noted, all functions that claim "Availability: Unix" are
-  supported on Mac OS X, which builds on a Unix core.
-
-.. Availability notes get their own line and occur at the end of the function
-.. documentation.
 
 .. note::
 
@@ -105,7 +96,7 @@ process and user.
 
    Return the filename corresponding to the controlling terminal of the process.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. data:: environ
@@ -235,7 +226,7 @@ process and user.
    and ``'surrogateescape'`` error handler. Use :func:`os.getenvb` if you
    would like to use a different encoding.
 
-   Availability: most flavors of Unix, Windows.
+   .. availability:: most flavors of Unix, Windows.
 
 
 .. function:: getenvb(key, default=None)
@@ -246,7 +237,7 @@ process and user.
    :func:`getenvb` is only available if :data:`supports_bytes_environ`
    is True.
 
-   Availability: most flavors of Unix.
+   .. availability:: most flavors of Unix.
 
    .. versionadded:: 3.2
 
@@ -267,7 +258,7 @@ process and user.
    Return the effective group id of the current process.  This corresponds to the
    "set id" bit on the file being executed in the current process.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: geteuid()
@@ -276,7 +267,7 @@ process and user.
 
    Return the current process's effective user id.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: getgid()
@@ -285,7 +276,7 @@ process and user.
 
    Return the real group id of the current process.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: getgrouplist(user, group)
@@ -294,7 +285,7 @@ process and user.
    list, it is included; typically, *group* is specified as the group ID
    field from the password record for *user*.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -303,7 +294,7 @@ process and user.
 
    Return list of supplemental group ids associated with the current process.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. note::
 
@@ -331,7 +322,7 @@ process and user.
    falls back to ``pwd.getpwuid(os.getuid())[0]`` to get the login name of the
    current real user id.
 
-   Availability: Unix, Windows.
+   .. availability:: Unix, Windows.
 
 
 .. function:: getpgid(pid)
@@ -339,7 +330,7 @@ process and user.
    Return the process group id of the process with process id *pid*. If *pid* is 0,
    the process group id of the current process is returned.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 .. function:: getpgrp()
 
@@ -347,7 +338,7 @@ process and user.
 
    Return the id of the current process group.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: getpid()
@@ -365,7 +356,7 @@ process and user.
    the id returned is the one of the init process (1), on Windows it is still
    the same id, which may be already reused by another process.
 
-   Availability: Unix, Windows.
+   .. availability:: Unix, Windows.
 
    .. versionchanged:: 3.2
       Added support for Windows.
@@ -383,7 +374,7 @@ process and user.
    (respectively) the calling process, the process group of the calling process,
    or the real user ID of the calling process.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -394,7 +385,7 @@ process and user.
 
    Parameters for the :func:`getpriority` and :func:`setpriority` functions.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -404,7 +395,7 @@ process and user.
    Return a tuple (ruid, euid, suid) denoting the current process's
    real, effective, and saved user ids.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.2
 
@@ -414,7 +405,7 @@ process and user.
    Return a tuple (rgid, egid, sgid) denoting the current process's
    real, effective, and saved group ids.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.2
 
@@ -425,7 +416,7 @@ process and user.
 
    Return the current process's real user id.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: initgroups(username, gid)
@@ -434,7 +425,7 @@ process and user.
    the groups of which the specified username is a member, plus the specified
    group id.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.2
 
@@ -447,7 +438,7 @@ process and user.
    changes to the environment affect subprocesses started with :func:`os.system`,
    :func:`popen` or :func:`fork` and :func:`execv`.
 
-   Availability: most flavors of Unix, Windows.
+   .. availability:: most flavors of Unix, Windows.
 
    .. note::
 
@@ -464,21 +455,21 @@ process and user.
 
    Set the current process's effective group id.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: seteuid(euid)
 
    Set the current process's effective user id.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: setgid(gid)
 
    Set the current process' group id.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: setgroups(groups)
@@ -487,7 +478,7 @@ process and user.
    *groups*. *groups* must be a sequence, and each element must be an integer
    identifying a group. This operation is typically available only to the superuser.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. note:: On Mac OS X, the length of *groups* may not exceed the
       system-defined maximum number of effective group ids, typically 16.
@@ -499,7 +490,7 @@ process and user.
    Call the system call :c:func:`setpgrp` or ``setpgrp(0, 0)`` depending on
    which version is implemented (if any).  See the Unix manual for the semantics.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: setpgid(pid, pgrp)
@@ -508,7 +499,7 @@ process and user.
    process with id *pid* to the process group with id *pgrp*.  See the Unix manual
    for the semantics.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: setpriority(which, who, priority)
@@ -525,7 +516,7 @@ process and user.
    *priority* is a value in the range -20 to 19. The default priority is 0;
    lower priorities cause more favorable scheduling.
 
-   Availability: Unix
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -534,14 +525,14 @@ process and user.
 
    Set the current process's real and effective group ids.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: setresgid(rgid, egid, sgid)
 
    Set the current process's real, effective, and saved group ids.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.2
 
@@ -550,7 +541,7 @@ process and user.
 
    Set the current process's real, effective, and saved user ids.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.2
 
@@ -559,21 +550,21 @@ process and user.
 
    Set the current process's real and effective user ids.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: getsid(pid)
 
    Call the system call :c:func:`getsid`.  See the Unix manual for the semantics.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: setsid()
 
    Call the system call :c:func:`setsid`.  See the Unix manual for the semantics.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: setuid(uid)
@@ -582,7 +573,7 @@ process and user.
 
    Set the current process's user id.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. placed in this section since it relates to errno.... a little weak
@@ -631,7 +622,7 @@ process and user.
    :func:`socket.gethostname`  or even
    ``socket.gethostbyaddr(socket.gethostname())``.
 
-   Availability: recent flavors of Unix.
+   .. availability:: recent flavors of Unix.
 
    .. versionchanged:: 3.3
       Return type changed from a tuple to a tuple-like object
@@ -651,7 +642,7 @@ process and user.
    calls to :func:`unsetenv` don't update ``os.environ``, so it is actually
    preferable to delete items of ``os.environ``.
 
-   Availability: most flavors of Unix, Windows.
+   .. availability:: most flavors of Unix, Windows.
 
 
 .. _os-newstreams:
@@ -754,7 +745,7 @@ as internal buffering of data.
    docs for :func:`chmod` for possible values of *mode*.  As of Python 3.3, this
    is equivalent to ``os.chmod(fd, mode)``.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: fchown(fd, uid, gid)
@@ -764,7 +755,7 @@ as internal buffering of data.
    :func:`chown`.  As of Python 3.3, this is equivalent to ``os.chown(fd, uid,
    gid)``.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: fdatasync(fd)
@@ -772,7 +763,7 @@ as internal buffering of data.
    Force write of file with filedescriptor *fd* to disk. Does not force update of
    metadata.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. note::
       This function is not available on MacOS.
@@ -795,7 +786,7 @@ as internal buffering of data.
 
    As of Python 3.3, this is equivalent to ``os.pathconf(fd, name)``.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: fstat(fd)
@@ -816,7 +807,7 @@ as internal buffering of data.
    file descriptor *fd*, like :func:`statvfs`.  As of Python 3.3, this is
    equivalent to ``os.statvfs(fd)``.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: fsync(fd)
@@ -828,7 +819,7 @@ as internal buffering of data.
    ``f.flush()``, and then do ``os.fsync(f.fileno())``, to ensure that all internal
    buffers associated with *f* are written to disk.
 
-   Availability: Unix, Windows.
+   .. availability:: Unix, Windows.
 
 
 .. function:: ftruncate(fd, length)
@@ -837,7 +828,7 @@ as internal buffering of data.
    most *length* bytes in size.  As of Python 3.3, this is equivalent to
    ``os.truncate(fd, length)``.
 
-   Availability: Unix, Windows.
+   .. availability:: Unix, Windows.
 
    .. versionchanged:: 3.5
       Added support for Windows
@@ -849,7 +840,7 @@ as internal buffering of data.
 
    See also :func:`set_blocking` and :meth:`socket.socket.setblocking`.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.5
 
@@ -867,7 +858,7 @@ as internal buffering of data.
    :data:`F_ULOCK` or :data:`F_TEST`.
    *len* specifies the section of the file to lock.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -879,7 +870,7 @@ as internal buffering of data.
 
    Flags that specify what action :func:`lockf` will take.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -1011,7 +1002,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
    descriptors are :ref:`non-inheritable <fd_inheritance>`. For a (slightly) more
    portable approach, use the :mod:`pty` module.
 
-   Availability: some flavors of Unix.
+   .. availability:: some flavors of Unix.
 
    .. versionchanged:: 3.4
       The new file descriptors are now non-inheritable.
@@ -1023,7 +1014,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
    reading and writing, respectively. The new file descriptor is
    :ref:`non-inheritable <fd_inheritance>`.
 
-   Availability: Unix, Windows.
+   .. availability:: Unix, Windows.
 
    .. versionchanged:: 3.4
       The new file descriptors are now non-inheritable.
@@ -1037,7 +1028,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
    Return a pair of file descriptors ``(r, w)`` usable for reading and writing,
    respectively.
 
-   Availability: some flavors of Unix.
+   .. availability:: some flavors of Unix.
 
    .. versionadded:: 3.3
 
@@ -1047,7 +1038,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
    Ensures that enough disk space is allocated for the file specified by *fd*
    starting from *offset* and continuing for *len* bytes.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -1062,7 +1053,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
    :data:`POSIX_FADV_RANDOM`, :data:`POSIX_FADV_NOREUSE`,
    :data:`POSIX_FADV_WILLNEED` or :data:`POSIX_FADV_DONTNEED`.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -1077,7 +1068,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
    Flags that can be used in *advice* in :func:`posix_fadvise` that specify
    the access pattern that is likely to be used.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -1090,7 +1081,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
    Return a bytestring containing the bytes read. If the end of the file
    referred to by *fd* has been reached, an empty bytes object is returned.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -1116,8 +1107,8 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
 
    Combine the functionality of :func:`os.readv` and :func:`os.pread`.
 
-   Availability: Linux 2.6.30 and newer, FreeBSD 6.0 and newer,
-   OpenBSD 2.7 and newer. Using flags requires Linux 4.6 or newer.
+   .. availability:: Linux 2.6.30 and newer, FreeBSD 6.0 and newer,
+      OpenBSD 2.7 and newer. Using flags requires Linux 4.6 or newer.
 
    .. versionadded:: 3.7
 
@@ -1132,7 +1123,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
    If no bytes were read, it will return ``-1`` and set errno to
    :data:`errno.EAGAIN`.
 
-   Availability: Linux 4.14 and newer.
+   .. availability:: Linux 4.14 and newer.
 
    .. versionadded:: 3.7
 
@@ -1146,7 +1137,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
    Currently, on Linux, this feature is usable only on a file descriptor opened
    using the :data:`O_DIRECT` flag.
 
-   Availability: Linux 4.6 and newer.
+   .. availability:: Linux 4.6 and newer.
 
    .. versionadded:: 3.7
 
@@ -1158,7 +1149,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
 
    Return the number of bytes actually written.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -1184,8 +1175,8 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
 
    Combine the functionality of :func:`os.writev` and :func:`os.pwrite`.
 
-   Availability: Linux 2.6.30 and newer, FreeBSD 6.0 and newer,
-   OpenBSD 2.7 and newer. Using flags requires Linux 4.7 or newer.
+   .. availability:: Linux 2.6.30 and newer, FreeBSD 6.0 and newer,
+      OpenBSD 2.7 and newer. Using flags requires Linux 4.7 or newer.
 
    .. versionadded:: 3.7
 
@@ -1195,7 +1186,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
    Provide a per-write equivalent of the :data:`O_DSYNC` ``open(2)`` flag. This
    flag effect applies only to the data range written by the system call.
 
-   Availability: Linux 4.7 and newer.
+   .. availability:: Linux 4.7 and newer.
 
    .. versionadded:: 3.7
 
@@ -1205,7 +1196,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
    Provide a per-write equivalent of the :data:`O_SYNC` ``open(2)`` flag. This
    flag effect applies only to the data range written by the system call.
 
-   Availability: Linux 4.7 and newer.
+   .. availability:: Linux 4.7 and newer.
 
    .. versionadded:: 3.7
 
@@ -1257,7 +1248,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
    Cross-platform applications should not use *headers*, *trailers* and *flags*
    arguments.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. note::
 
@@ -1274,7 +1265,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
 
    See also :func:`get_blocking` and :meth:`socket.socket.setblocking`.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.5
 
@@ -1286,7 +1277,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
    Parameters to the :func:`sendfile` function, if the implementation supports
    them.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -1304,7 +1295,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
    The operating system may set a limit (:func:`sysconf` value
    ``'SC_IOV_MAX'``) on the number of buffers that can be used.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -1314,7 +1305,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
    Return the process group associated with the terminal given by *fd* (an open
    file descriptor as returned by :func:`os.open`).
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: tcsetpgrp(fd, pg)
@@ -1322,7 +1313,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
    Set the process group associated with the terminal given by *fd* (an open file
    descriptor as returned by :func:`os.open`) to *pg*.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: ttyname(fd)
@@ -1331,7 +1322,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
    file descriptor *fd*.  If *fd* is not associated with a terminal device, an
    exception is raised.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: write(fd, str)
@@ -1366,7 +1357,7 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
    The operating system may set a limit (:func:`sysconf` value
    ``'SC_IOV_MAX'``) on the number of buffers that can be used.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -1393,7 +1384,7 @@ Querying the size of a terminal
    should normally be used, ``os.get_terminal_size`` is the low-level
    implementation.
 
-   Availability: Unix, Windows.
+   .. availability:: Unix, Windows.
 
 .. class:: terminal_size
 
@@ -1442,13 +1433,13 @@ streams are closed, and inheritable handles are only inherited if the
 
    Get the "inheritable" flag of the specified handle (a boolean).
 
-   Availability: Windows.
+   .. availability:: Windows.
 
 .. function:: set_handle_inheritable(handle, inheritable)
 
    Set the "inheritable" flag of the specified handle.
 
-   Availability: Windows.
+   .. availability:: Windows.
 
 
 .. _os-file-dir:
@@ -1603,7 +1594,7 @@ features:
 
    This function can support :ref:`not following symlinks <follow_symlinks>`.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
       The *follow_symlinks* argument.
@@ -1668,7 +1659,7 @@ features:
    See :func:`shutil.chown` for a higher-level function that accepts names in
    addition to numeric ids.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
       Added support for specifying an open file descriptor for *path*,
@@ -1682,7 +1673,7 @@ features:
 
    Change the root directory of the current process to *path*.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionchanged:: 3.6
       Accepts a :term:`path-like object`.
@@ -1694,7 +1685,7 @@ features:
    descriptor *fd*.  The descriptor must refer to an opened directory, not an
    open file.  As of Python 3.3, this is equivalent to ``os.chdir(fd)``.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: getcwd()
@@ -1713,7 +1704,7 @@ features:
    not follow symbolic links.  As of Python 3.3, this is equivalent to
    ``os.chflags(path, flags, follow_symlinks=False)``.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionchanged:: 3.6
       Accepts a :term:`path-like object`.
@@ -1726,7 +1717,7 @@ features:
    for possible values of *mode*.  As of Python 3.3, this is equivalent to
    ``os.chmod(path, mode, follow_symlinks=False)``.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionchanged:: 3.6
       Accepts a :term:`path-like object`.
@@ -1737,7 +1728,7 @@ features:
    function will not follow symbolic links.  As of Python 3.3, this is equivalent
    to ``os.chown(path, uid, gid, follow_symlinks=False)``.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionchanged:: 3.6
       Accepts a :term:`path-like object`.
@@ -1751,7 +1742,7 @@ features:
    supply :ref:`paths relative to directory descriptors <dir_fd>`, and :ref:`not
    following symlinks <follow_symlinks>`.
 
-   Availability: Unix, Windows.
+   .. availability:: Unix, Windows.
 
    .. versionchanged:: 3.2
       Added Windows support.
@@ -1909,7 +1900,7 @@ features:
    FIFO for reading, and the client opens it for writing.  Note that :func:`mkfifo`
    doesn't open the FIFO --- it just creates the rendezvous point.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
       The *dir_fd* argument.
@@ -1931,7 +1922,7 @@ features:
    This function can also support :ref:`paths relative to directory descriptors
    <dir_fd>`.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
       The *dir_fd* argument.
@@ -1975,7 +1966,7 @@ features:
    This function can support :ref:`specifying a file descriptor
    <path_fd>`.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionchanged:: 3.6
       Accepts a :term:`path-like object`.
@@ -1987,7 +1978,7 @@ features:
    the integer values defined for those names by the host operating system.  This
    can be used to determine the set of names known to the system.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: readlink(path, *, dir_fd=None)
@@ -2005,7 +1996,7 @@ features:
    This function can also support :ref:`paths relative to directory descriptors
    <dir_fd>`.
 
-   Availability: Unix, Windows
+   .. availability:: Unix, Windows.
 
    .. versionchanged:: 3.2
       Added support for Windows 6.0 (Vista) symbolic links.
@@ -2594,7 +2585,7 @@ features:
 
    This function can support :ref:`specifying a file descriptor <path_fd>`.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionchanged:: 3.2
       The :const:`ST_RDONLY` and :const:`ST_NOSUID` constants were added.
@@ -2704,7 +2695,7 @@ features:
    morph to the target dynamically.  If the target is present, the type of the
    symlink will be created to match. Otherwise, the symlink will be created
    as a directory if *target_is_directory* is ``True`` or a file symlink (the
-   default) otherwise.  On non-Window platforms, *target_is_directory* is ignored.
+   default) otherwise.  On non-Windows platforms, *target_is_directory* is ignored.
 
    Symbolic link support was introduced in Windows 6.0 (Vista).  :func:`symlink`
    will raise a :exc:`NotImplementedError` on Windows versions earlier than 6.0.
@@ -2724,7 +2715,7 @@ features:
       :exc:`OSError` is raised when the function is called by an unprivileged
       user.
 
-   Availability: Unix, Windows.
+   .. availability:: Unix, Windows.
 
    .. versionchanged:: 3.2
       Added support for Windows 6.0 (Vista) symbolic links.
@@ -2741,7 +2732,7 @@ features:
 
    Force write of everything to disk.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -2753,7 +2744,7 @@ features:
 
    This function can support :ref:`specifying a file descriptor <path_fd>`.
 
-   Availability: Unix, Windows.
+   .. availability:: Unix, Windows.
 
    .. versionadded:: 3.3
 
@@ -2962,7 +2953,7 @@ features:
           for name in dirs:
               os.rmdir(name, dir_fd=rootfd)
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -3139,7 +3130,7 @@ to be ignored.
    you can check whether or not it is available using :data:`os.supports_fd`.
    If it is unavailable, using it will raise a :exc:`NotImplementedError`.
 
-   Availability: Unix, Windows.
+   .. availability:: Unix, Windows.
 
    .. versionadded:: 3.3
       Added support for specifying an open file descriptor for *path*
@@ -3173,7 +3164,7 @@ written in Python, such as a mail server's external command delivery program.
 
    Exit code that means no error occurred.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. data:: EX_USAGE
@@ -3181,49 +3172,49 @@ written in Python, such as a mail server's external command delivery program.
    Exit code that means the command was used incorrectly, such as when the wrong
    number of arguments are given.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. data:: EX_DATAERR
 
    Exit code that means the input data was incorrect.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. data:: EX_NOINPUT
 
    Exit code that means an input file did not exist or was not readable.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. data:: EX_NOUSER
 
    Exit code that means a specified user did not exist.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. data:: EX_NOHOST
 
    Exit code that means a specified host did not exist.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. data:: EX_UNAVAILABLE
 
    Exit code that means that a required service is unavailable.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. data:: EX_SOFTWARE
 
    Exit code that means an internal software error was detected.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. data:: EX_OSERR
@@ -3231,7 +3222,7 @@ written in Python, such as a mail server's external command delivery program.
    Exit code that means an operating system error was detected, such as the
    inability to fork or create a pipe.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. data:: EX_OSFILE
@@ -3239,21 +3230,21 @@ written in Python, such as a mail server's external command delivery program.
    Exit code that means some system file did not exist, could not be opened, or had
    some other kind of error.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. data:: EX_CANTCREAT
 
    Exit code that means a user specified output file could not be created.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. data:: EX_IOERR
 
    Exit code that means that an error occurred while doing I/O on some file.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. data:: EX_TEMPFAIL
@@ -3262,7 +3253,7 @@ written in Python, such as a mail server's external command delivery program.
    that may not really be an error, such as a network connection that couldn't be
    made during a retryable operation.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. data:: EX_PROTOCOL
@@ -3270,7 +3261,7 @@ written in Python, such as a mail server's external command delivery program.
    Exit code that means that a protocol exchange was illegal, invalid, or not
    understood.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. data:: EX_NOPERM
@@ -3278,21 +3269,21 @@ written in Python, such as a mail server's external command delivery program.
    Exit code that means that there were insufficient permissions to perform the
    operation (but not intended for file system problems).
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. data:: EX_CONFIG
 
    Exit code that means that some kind of configuration error occurred.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. data:: EX_NOTFOUND
 
    Exit code that means something like "an entry was not found".
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: fork()
@@ -3307,7 +3298,7 @@ written in Python, such as a mail server's external command delivery program.
 
       See :mod:`ssl` for applications that use the SSL module with fork().
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: forkpty()
@@ -3318,7 +3309,7 @@ written in Python, such as a mail server's external command delivery program.
    master end of the pseudo-terminal.  For a more portable approach, use the
    :mod:`pty` module.  If an error occurs :exc:`OSError` is raised.
 
-   Availability: some flavors of Unix.
+   .. availability:: some flavors of Unix.
 
 
 .. function:: kill(pid, sig)
@@ -3352,14 +3343,14 @@ written in Python, such as a mail server's external command delivery program.
 
    Send the signal *sig* to the process group *pgid*.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: nice(increment)
 
    Add *increment* to the process's "niceness".  Return the new niceness.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: plock(op)
@@ -3367,7 +3358,7 @@ written in Python, such as a mail server's external command delivery program.
    Lock program segments into memory.  The value of *op* (defined in
    ``<sys/lock.h>``) determines which segments are locked.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: popen(cmd, mode='r', buffering=-1)
@@ -3422,7 +3413,7 @@ written in Python, such as a mail server's external command delivery program.
 
    There is no way to unregister a function.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.7
 
@@ -3486,10 +3477,10 @@ written in Python, such as a mail server's external command delivery program.
       L = ['cp', 'index.html', '/dev/null']
       os.spawnvpe(os.P_WAIT, 'cp', L, os.environ)
 
-   Availability: Unix, Windows.  :func:`spawnlp`, :func:`spawnlpe`, :func:`spawnvp`
-   and :func:`spawnvpe` are not available on Windows.  :func:`spawnle` and
-   :func:`spawnve` are not thread-safe on Windows; we advise you to use the
-   :mod:`subprocess` module instead.
+   .. availability:: Unix, Windows.  :func:`spawnlp`, :func:`spawnlpe`, :func:`spawnvp`
+      and :func:`spawnvpe` are not available on Windows.  :func:`spawnle` and
+      :func:`spawnve` are not thread-safe on Windows; we advise you to use the
+      :mod:`subprocess` module instead.
 
    .. versionchanged:: 3.6
       Accepts a :term:`path-like object`.
@@ -3503,7 +3494,7 @@ written in Python, such as a mail server's external command delivery program.
    will return as soon as the new process has been created, with the process id as
    the return value.
 
-   Availability: Unix, Windows.
+   .. availability:: Unix, Windows.
 
 
 .. data:: P_WAIT
@@ -3514,7 +3505,7 @@ written in Python, such as a mail server's external command delivery program.
    of the process the run is successful, or ``-signal`` if a signal kills the
    process.
 
-   Availability: Unix, Windows.
+   .. availability:: Unix, Windows.
 
 
 .. data:: P_DETACH
@@ -3526,7 +3517,7 @@ written in Python, such as a mail server's external command delivery program.
    console of the calling process. If :const:`P_OVERLAY` is used, the current
    process will be replaced; the :func:`spawn\* <spawnl>` function will not return.
 
-   Availability: Windows.
+   .. availability:: Windows.
 
 
 .. function:: startfile(path[, operation])
@@ -3555,7 +3546,7 @@ written in Python, such as a mail server's external command delivery program.
    function is not resolved until this function is first called.  If the function
    cannot be resolved, :exc:`NotImplementedError` will be raised.
 
-   Availability: Windows.
+   .. availability:: Windows.
 
 
 .. function:: system(command)
@@ -3582,7 +3573,7 @@ written in Python, such as a mail server's external command delivery program.
    to using this function.  See the :ref:`subprocess-replacements` section in
    the :mod:`subprocess` documentation for some helpful recipes.
 
-   Availability: Unix, Windows.
+   .. availability:: Unix, Windows.
 
 
 .. function:: times()
@@ -3605,7 +3596,7 @@ written in Python, such as a mail server's external command delivery program.
    On Windows, only :attr:`user` and :attr:`system` are known; the other
    attributes are zero.
 
-   Availability: Unix, Windows.
+   .. availability:: Unix, Windows.
 
    .. versionchanged:: 3.3
       Return type changed from a tuple to a tuple-like object
@@ -3620,7 +3611,7 @@ written in Python, such as a mail server's external command delivery program.
    number is zero); the high bit of the low byte is set if a core file was
    produced.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 .. function:: waitid(idtype, id, options)
 
@@ -3635,7 +3626,7 @@ written in Python, such as a mail server's external command delivery program.
    :attr:`si_code` or ``None`` if :data:`WNOHANG` is specified and there are no
    children in a waitable state.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -3646,7 +3637,7 @@ written in Python, such as a mail server's external command delivery program.
    These are the possible values for *idtype* in :func:`waitid`. They affect
    how *id* is interpreted.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -3657,7 +3648,7 @@ written in Python, such as a mail server's external command delivery program.
    Flags that can be used in *options* in :func:`waitid` that specify what
    child signal to wait for.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -3670,7 +3661,7 @@ written in Python, such as a mail server's external command delivery program.
    These are the possible values for :attr:`si_code` in the result returned by
    :func:`waitid`.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -3711,13 +3702,13 @@ written in Python, such as a mail server's external command delivery program.
 .. function:: wait3(options)
 
    Similar to :func:`waitpid`, except no process id argument is given and a
-   3-element tuple containing the child's process id, exit status indication, and
-   resource usage information is returned.  Refer to :mod:`resource`.\
-   :func:`~resource.getrusage` for details on resource usage information.  The
-   option argument is the same as that provided to :func:`waitpid` and
-   :func:`wait4`.
+   3-element tuple containing the child's process id, exit status indication,
+   and resource usage information is returned.  Refer to
+   :mod:`resource`.\ :func:`~resource.getrusage` for details on resource usage
+   information.  The option argument is the same as that provided to
+   :func:`waitpid` and :func:`wait4`.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: wait4(pid, options)
@@ -3728,7 +3719,7 @@ written in Python, such as a mail server's external command delivery program.
    resource usage information.  The arguments to :func:`wait4` are the same
    as those provided to :func:`waitpid`.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. data:: WNOHANG
@@ -3736,7 +3727,7 @@ written in Python, such as a mail server's external command delivery program.
    The option for :func:`waitpid` to return immediately if no child process status
    is available immediately. The function returns ``(0, 0)`` in this case.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. data:: WCONTINUED
@@ -3744,7 +3735,7 @@ written in Python, such as a mail server's external command delivery program.
    This option causes child processes to be reported if they have been continued
    from a job control stop since their status was last reported.
 
-   Availability: some Unix systems.
+   .. availability:: some Unix systems.
 
 
 .. data:: WUNTRACED
@@ -3752,7 +3743,7 @@ written in Python, such as a mail server's external command delivery program.
    This option causes child processes to be reported if they have been stopped but
    their current state has not been reported since they were stopped.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 The following functions take a process status code as returned by
@@ -3764,7 +3755,7 @@ used to determine the disposition of a process.
    Return ``True`` if a core dump was generated for the process, otherwise
    return ``False``.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: WIFCONTINUED(status)
@@ -3772,7 +3763,7 @@ used to determine the disposition of a process.
    Return ``True`` if the process has been continued from a job control stop,
    otherwise return ``False``.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: WIFSTOPPED(status)
@@ -3780,7 +3771,7 @@ used to determine the disposition of a process.
    Return ``True`` if the process has been stopped, otherwise return
    ``False``.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: WIFSIGNALED(status)
@@ -3788,7 +3779,7 @@ used to determine the disposition of a process.
    Return ``True`` if the process exited due to a signal, otherwise return
    ``False``.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: WIFEXITED(status)
@@ -3796,7 +3787,7 @@ used to determine the disposition of a process.
    Return ``True`` if the process exited using the :manpage:`exit(2)` system call,
    otherwise return ``False``.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: WEXITSTATUS(status)
@@ -3804,21 +3795,21 @@ used to determine the disposition of a process.
    If ``WIFEXITED(status)`` is true, return the integer parameter to the
    :manpage:`exit(2)` system call.  Otherwise, the return value is meaningless.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: WSTOPSIG(status)
 
    Return the signal which caused the process to stop.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: WTERMSIG(status)
 
    Return the signal which caused the process to exit.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 Interface to the scheduler
@@ -3964,7 +3955,7 @@ Miscellaneous System Information
    included in ``confstr_names``, an :exc:`OSError` is raised with
    :const:`errno.EINVAL` for the error number.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. data:: confstr_names
@@ -3973,7 +3964,7 @@ Miscellaneous System Information
    defined for those names by the host operating system. This can be used to
    determine the set of names known to the system.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: cpu_count()
@@ -3994,7 +3985,7 @@ Miscellaneous System Information
    1, 5, and 15 minutes or raises :exc:`OSError` if the load average was
    unobtainable.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. function:: sysconf(name)
@@ -4004,7 +3995,7 @@ Miscellaneous System Information
    the *name* parameter for :func:`confstr` apply here as well; the dictionary that
    provides information on the known names is given by ``sysconf_names``.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 
 .. data:: sysconf_names
@@ -4013,7 +4004,7 @@ Miscellaneous System Information
    defined for those names by the host operating system. This can be used to
    determine the set of names known to the system.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 The following data values are used to support path manipulation operations.  These
 are defined for all platforms.
@@ -4021,6 +4012,7 @@ are defined for all platforms.
 Higher-level operations on pathnames are defined in the :mod:`os.path` module.
 
 
+.. index:: single: . (dot); in pathnames
 .. data:: curdir
 
    The constant string used by the operating system to refer to the current
@@ -4028,6 +4020,7 @@ Higher-level operations on pathnames are defined in the :mod:`os.path` module.
    :mod:`os.path`.
 
 
+.. index:: single: ..; in pathnames
 .. data:: pardir
 
    The constant string used by the operating system to refer to the parent
@@ -4035,6 +4028,8 @@ Higher-level operations on pathnames are defined in the :mod:`os.path` module.
    :mod:`os.path`.
 
 
+.. index:: single: / (slash); in pathnames
+.. index:: single: \ (backslash); in pathnames (Windows)
 .. data:: sep
 
    The character used by the operating system to separate pathname components.
@@ -4044,6 +4039,7 @@ Higher-level operations on pathnames are defined in the :mod:`os.path` module.
    useful. Also available via :mod:`os.path`.
 
 
+.. index:: single: / (slash); in pathnames
 .. data:: altsep
 
    An alternative character used by the operating system to separate pathname
@@ -4052,12 +4048,15 @@ Higher-level operations on pathnames are defined in the :mod:`os.path` module.
    :mod:`os.path`.
 
 
+.. index:: single: . (dot); in pathnames
 .. data:: extsep
 
    The character which separates the base filename from the extension; for example,
    the ``'.'`` in :file:`os.py`. Also available via :mod:`os.path`.
 
 
+.. index:: single: : (colon); path separator (POSIX)
+   single: ; (semicolon)
 .. data:: pathsep
 
    The character conventionally used by the operating system to separate search
@@ -4125,7 +4124,7 @@ Random numbers
    See also the `Linux getrandom() manual page
    <http://man7.org/linux/man-pages/man2/getrandom.2.html>`_.
 
-   Availability: Linux 3.17 and newer.
+   .. availability:: Linux 3.17 and newer.
 
    .. versionadded:: 3.6
 
index 522bb7e..a7d3dac 100644 (file)
@@ -153,8 +153,7 @@ and (read-only) attributes:
    number of bytes written.  If the audio device is in blocking mode (the
    default), the entire data is always written (again, this is different from
    usual Unix device semantics).  If the device is in non-blocking mode, some
-   data may not be written
-   ---see :meth:`writeall`.
+   data may not be written---see :meth:`writeall`.
 
    .. versionchanged:: 3.5
       Writable :term:`bytes-like object` is now accepted.
index 2b10ee2..36f66a1 100644 (file)
@@ -71,7 +71,9 @@ The :mod:`pickle` module differs from :mod:`marshal` in several significant ways
   :file:`.pyc` files, the Python implementers reserve the right to change the
   serialization format in non-backwards compatible ways should the need arise.
   The :mod:`pickle` serialization format is guaranteed to be backwards compatible
-  across Python releases.
+  across Python releases provided a compatible pickle protocol is chosen and
+  pickling and unpickling code deals with Python 2 to Python 3 type differences
+  if your data is crossing that unique breaking change language boundary.
 
 Comparison with ``json``
 ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -235,6 +237,9 @@ process more convenient:
    *errors* tell pickle how to decode 8-bit string instances pickled by Python
    2; these default to 'ASCII' and 'strict', respectively.  The *encoding* can
    be 'bytes' to read these 8-bit string instances as bytes objects.
+   Using ``encoding='latin1'`` is required for unpickling NumPy arrays and
+   instances of :class:`~datetime.datetime`, :class:`~datetime.date` and
+   :class:`~datetime.time` pickled by Python 2.
 
 .. function:: loads(bytes_object, \*, fix_imports=True, encoding="ASCII", errors="strict")
 
@@ -252,6 +257,9 @@ process more convenient:
    *errors* tell pickle how to decode 8-bit string instances pickled by Python
    2; these default to 'ASCII' and 'strict', respectively.  The *encoding* can
    be 'bytes' to read these 8-bit string instances as bytes objects.
+   Using ``encoding='latin1'`` is required for unpickling NumPy arrays and
+   instances of :class:`~datetime.datetime`, :class:`~datetime.date` and
+   :class:`~datetime.time` pickled by Python 2.
 
 
 The :mod:`pickle` module defines three exceptions:
index aa97d3e..deadf18 100644 (file)
@@ -30,6 +30,8 @@ The :mod:`pprint` module defines one class:
 .. First the implementation class:
 
 
+.. index:: single: ...; placeholder
+
 .. class:: PrettyPrinter(indent=1, width=80, depth=None, stream=None, *, \
                          compact=False)
 
@@ -217,135 +219,156 @@ let's fetch information about a project from `PyPI <https://pypi.org>`_::
    >>> import json
    >>> import pprint
    >>> from urllib.request import urlopen
-   >>> with urlopen('http://pypi.org/project/Twisted/json') as url:
-   ...     http_info = url.info()
-   ...     raw_data = url.read().decode(http_info.get_content_charset())
-   >>> project_info = json.loads(raw_data)
+   >>> with urlopen('https://pypi.org/pypi/sampleproject/json') as resp:
+   ...     project_info = json.load(resp)['info']
 
 In its basic form, :func:`pprint` shows the whole object::
 
    >>> pprint.pprint(project_info)
-   {'info': {'_pypi_hidden': False,
-             '_pypi_ordering': 125,
-             'author': 'Glyph Lefkowitz',
-             'author_email': 'glyph@twistedmatrix.com',
-             'bugtrack_url': '',
-             'cheesecake_code_kwalitee_id': None,
-             'cheesecake_documentation_id': None,
-             'cheesecake_installability_id': None,
-             'classifiers': ['Programming Language :: Python :: 2.6',
-                             'Programming Language :: Python :: 2.7',
-                             'Programming Language :: Python :: 2 :: Only'],
-             'description': 'An extensible framework for Python programming, with '
-                            'special focus\r\n'
-                            'on event-based network programming and multiprotocol '
-                            'integration.',
-             'docs_url': '',
-             'download_url': 'UNKNOWN',
-             'home_page': 'http://twistedmatrix.com/',
-             'keywords': '',
-             'license': 'MIT',
-             'maintainer': '',
-             'maintainer_email': '',
-             'name': 'Twisted',
-             'package_url': 'http://pypi.org/project/Twisted',
-             'platform': 'UNKNOWN',
-             'release_url': 'http://pypi.org/project/Twisted/12.3.0',
-             'requires_python': None,
-             'stable_version': None,
-             'summary': 'An asynchronous networking framework written in Python',
-             'version': '12.3.0'},
-    'urls': [{'comment_text': '',
-              'downloads': 71844,
-              'filename': 'Twisted-12.3.0.tar.bz2',
-              'has_sig': False,
-              'md5_digest': '6e289825f3bf5591cfd670874cc0862d',
-              'packagetype': 'sdist',
-              'python_version': 'source',
-              'size': 2615733,
-              'upload_time': '2012-12-26T12:47:03',
-              'url': 'https://pypi.org/packages/source/T/Twisted/Twisted-12.3.0.tar.bz2'},
-             {'comment_text': '',
-              'downloads': 5224,
-              'filename': 'Twisted-12.3.0.win32-py2.7.msi',
-              'has_sig': False,
-              'md5_digest': '6b778f5201b622a5519a2aca1a2fe512',
-              'packagetype': 'bdist_msi',
-              'python_version': '2.7',
-              'size': 2916352,
-              'upload_time': '2012-12-26T12:48:15',
-              'url': 'https://pypi.org/packages/2.7/T/Twisted/Twisted-12.3.0.win32-py2.7.msi'}]}
+   {'author': 'The Python Packaging Authority',
+    'author_email': 'pypa-dev@googlegroups.com',
+    'bugtrack_url': None,
+    'classifiers': ['Development Status :: 3 - Alpha',
+                    'Intended Audience :: Developers',
+                    'License :: OSI Approved :: MIT License',
+                    'Programming Language :: Python :: 2',
+                    'Programming Language :: Python :: 2.6',
+                    'Programming Language :: Python :: 2.7',
+                    'Programming Language :: Python :: 3',
+                    'Programming Language :: Python :: 3.2',
+                    'Programming Language :: Python :: 3.3',
+                    'Programming Language :: Python :: 3.4',
+                    'Topic :: Software Development :: Build Tools'],
+    'description': 'A sample Python project\n'
+                   '=======================\n'
+                   '\n'
+                   'This is the description file for the project.\n'
+                   '\n'
+                   'The file should use UTF-8 encoding and be written using '
+                   'ReStructured Text. It\n'
+                   'will be used to generate the project webpage on PyPI, and '
+                   'should be written for\n'
+                   'that purpose.\n'
+                   '\n'
+                   'Typical contents for this file would include an overview of '
+                   'the project, basic\n'
+                   'usage examples, etc. Generally, including the project '
+                   'changelog in here is not\n'
+                   'a good idea, although a simple "What\'s New" section for the '
+                   'most recent version\n'
+                   'may be appropriate.',
+    'description_content_type': None,
+    'docs_url': None,
+    'download_url': 'UNKNOWN',
+    'downloads': {'last_day': -1, 'last_month': -1, 'last_week': -1},
+    'home_page': 'https://github.com/pypa/sampleproject',
+    'keywords': 'sample setuptools development',
+    'license': 'MIT',
+    'maintainer': None,
+    'maintainer_email': None,
+    'name': 'sampleproject',
+    'package_url': 'https://pypi.org/project/sampleproject/',
+    'platform': 'UNKNOWN',
+    'project_url': 'https://pypi.org/project/sampleproject/',
+    'project_urls': {'Download': 'UNKNOWN',
+                     'Homepage': 'https://github.com/pypa/sampleproject'},
+    'release_url': 'https://pypi.org/project/sampleproject/1.2.0/',
+    'requires_dist': None,
+    'requires_python': None,
+    'summary': 'A sample Python project',
+    'version': '1.2.0'}
 
 The result can be limited to a certain *depth* (ellipsis is used for deeper
 contents)::
 
-   >>> pprint.pprint(project_info, depth=2)
-   {'info': {'_pypi_hidden': False,
-             '_pypi_ordering': 125,
-             'author': 'Glyph Lefkowitz',
-             'author_email': 'glyph@twistedmatrix.com',
-             'bugtrack_url': '',
-             'cheesecake_code_kwalitee_id': None,
-             'cheesecake_documentation_id': None,
-             'cheesecake_installability_id': None,
-             'classifiers': [...],
-             'description': 'An extensible framework for Python programming, with '
-                            'special focus\r\n'
-                            'on event-based network programming and multiprotocol '
-                            'integration.',
-             'docs_url': '',
-             'download_url': 'UNKNOWN',
-             'home_page': 'http://twistedmatrix.com/',
-             'keywords': '',
-             'license': 'MIT',
-             'maintainer': '',
-             'maintainer_email': '',
-             'name': 'Twisted',
-             'package_url': 'http://pypi.org/project/Twisted',
-             'platform': 'UNKNOWN',
-             'release_url': 'http://pypi.org/project/Twisted/12.3.0',
-             'requires_python': None,
-             'stable_version': None,
-             'summary': 'An asynchronous networking framework written in Python',
-             'version': '12.3.0'},
-    'urls': [{...}, {...}]}
+   >>> pprint.pprint(project_info, depth=1)
+   {'author': 'The Python Packaging Authority',
+    'author_email': 'pypa-dev@googlegroups.com',
+    'bugtrack_url': None,
+    'classifiers': [...],
+    'description': 'A sample Python project\n'
+                   '=======================\n'
+                   '\n'
+                   'This is the description file for the project.\n'
+                   '\n'
+                   'The file should use UTF-8 encoding and be written using '
+                   'ReStructured Text. It\n'
+                   'will be used to generate the project webpage on PyPI, and '
+                   'should be written for\n'
+                   'that purpose.\n'
+                   '\n'
+                   'Typical contents for this file would include an overview of '
+                   'the project, basic\n'
+                   'usage examples, etc. Generally, including the project '
+                   'changelog in here is not\n'
+                   'a good idea, although a simple "What\'s New" section for the '
+                   'most recent version\n'
+                   'may be appropriate.',
+    'description_content_type': None,
+    'docs_url': None,
+    'download_url': 'UNKNOWN',
+    'downloads': {...},
+    'home_page': 'https://github.com/pypa/sampleproject',
+    'keywords': 'sample setuptools development',
+    'license': 'MIT',
+    'maintainer': None,
+    'maintainer_email': None,
+    'name': 'sampleproject',
+    'package_url': 'https://pypi.org/project/sampleproject/',
+    'platform': 'UNKNOWN',
+    'project_url': 'https://pypi.org/project/sampleproject/',
+    'project_urls': {...},
+    'release_url': 'https://pypi.org/project/sampleproject/1.2.0/',
+    'requires_dist': None,
+    'requires_python': None,
+    'summary': 'A sample Python project',
+    'version': '1.2.0'}
 
 Additionally, maximum character *width* can be suggested. If a long object
 cannot be split, the specified width will be exceeded::
 
-   >>> pprint.pprint(project_info, depth=2, width=50)
-   {'info': {'_pypi_hidden': False,
-             '_pypi_ordering': 125,
-             'author': 'Glyph Lefkowitz',
-             'author_email': 'glyph@twistedmatrix.com',
-             'bugtrack_url': '',
-             'cheesecake_code_kwalitee_id': None,
-             'cheesecake_documentation_id': None,
-             'cheesecake_installability_id': None,
-             'classifiers': [...],
-             'description': 'An extensible '
-                            'framework for Python '
-                            'programming, with '
-                            'special focus\r\n'
-                            'on event-based network '
-                            'programming and '
-                            'multiprotocol '
-                            'integration.',
-             'docs_url': '',
-             'download_url': 'UNKNOWN',
-             'home_page': 'http://twistedmatrix.com/',
-             'keywords': '',
-             'license': 'MIT',
-             'maintainer': '',
-             'maintainer_email': '',
-             'name': 'Twisted',
-             'package_url': 'http://pypi.org/project/Twisted',
-             'platform': 'UNKNOWN',
-             'release_url': 'http://pypi.org/project/Twisted/12.3.0',
-             'requires_python': None,
-             'stable_version': None,
-             'summary': 'An asynchronous networking '
-                        'framework written in '
-                        'Python',
-             'version': '12.3.0'},
-    'urls': [{...}, {...}]}
+   >>> pprint.pprint(project_info, depth=1, width=60)
+   {'author': 'The Python Packaging Authority',
+    'author_email': 'pypa-dev@googlegroups.com',
+    'bugtrack_url': None,
+    'classifiers': [...],
+    'description': 'A sample Python project\n'
+                   '=======================\n'
+                   '\n'
+                   'This is the description file for the '
+                   'project.\n'
+                   '\n'
+                   'The file should use UTF-8 encoding and be '
+                   'written using ReStructured Text. It\n'
+                   'will be used to generate the project '
+                   'webpage on PyPI, and should be written '
+                   'for\n'
+                   'that purpose.\n'
+                   '\n'
+                   'Typical contents for this file would '
+                   'include an overview of the project, '
+                   'basic\n'
+                   'usage examples, etc. Generally, including '
+                   'the project changelog in here is not\n'
+                   'a good idea, although a simple "What\'s '
+                   'New" section for the most recent version\n'
+                   'may be appropriate.',
+    'description_content_type': None,
+    'docs_url': None,
+    'download_url': 'UNKNOWN',
+    'downloads': {...},
+    'home_page': 'https://github.com/pypa/sampleproject',
+    'keywords': 'sample setuptools development',
+    'license': 'MIT',
+    'maintainer': None,
+    'maintainer_email': None,
+    'name': 'sampleproject',
+    'package_url': 'https://pypi.org/project/sampleproject/',
+    'platform': 'UNKNOWN',
+    'project_url': 'https://pypi.org/project/sampleproject/',
+    'project_urls': {...},
+    'release_url': 'https://pypi.org/project/sampleproject/1.2.0/',
+    'requires_dist': None,
+    'requires_python': None,
+    'summary': 'A sample Python project',
+    'version': '1.2.0'}
index d720e01..8cb5a4d 100644 (file)
@@ -54,10 +54,10 @@ byte-code cache files in the directory containing the source code.
    level of the current interpreter.
 
    *invalidation_mode* should be a member of the :class:`PycInvalidationMode`
-   enum and controls how the generated ``.pyc`` files are invalidated at
-   runtime. If the :envvar:`SOURCE_DATE_EPOCH` environment variable is set,
-   *invalidation_mode* will be forced to
-   :attr:`PycInvalidationMode.CHECKED_HASH`.
+   enum and controls how the generated bytecode cache is invalidated at
+   runtime.  The default is :attr:`PycInvalidationMode.CHECKED_HASH` if
+   the :envvar:`SOURCE_DATE_EPOCH` environment variable is set, otherwise
+   the default is :attr:`PycInvalidationMode.TIMESTAMP`.
 
    .. versionchanged:: 3.2
       Changed default value of *cfile* to be :PEP:`3147`-compliant.  Previous
@@ -77,6 +77,11 @@ byte-code cache files in the directory containing the source code.
       *invalidation_mode* will be forced to
       :attr:`PycInvalidationMode.CHECKED_HASH`.
 
+   .. versionchanged:: 3.7.2
+      The :envvar:`SOURCE_DATE_EPOCH` environment variable no longer
+      overrides the value of the *invalidation_mode* argument, and determines
+      its default value instead.
+
 
 .. class:: PycInvalidationMode
 
index 1520faa..7335a64 100644 (file)
@@ -28,8 +28,8 @@ competing threads; however, they are not designed to handle reentrancy
 within a thread.
 
 In addition, the module implements a "simple"
-:abbr:`FIFO (first-in, first-out)` queue type where
-specific implementations can provide additional guarantees
+:abbr:`FIFO (first-in, first-out)` queue type, :class:`SimpleQueue`, whose
+specific implementation provides additional guarantees
 in exchange for the smaller functionality.
 
 The :mod:`queue` module defines the following classes and exceptions:
index cc3516a..e6455bb 100644 (file)
@@ -93,15 +93,21 @@ the expression ``(?:a{6})*`` matches any multiple of six ``'a'`` characters.
 
 The special characters are:
 
+.. index:: single: . (dot); in regular expressions
+
 ``.``
    (Dot.)  In the default mode, this matches any character except a newline.  If
    the :const:`DOTALL` flag has been specified, this matches any character
    including a newline.
 
+.. index:: single: ^ (caret); in regular expressions
+
 ``^``
    (Caret.)  Matches the start of the string, and in :const:`MULTILINE` mode also
    matches immediately after each newline.
 
+.. index:: single: $ (dollar); in regular expressions
+
 ``$``
    Matches the end of the string or just before the newline at the end of the
    string, and in :const:`MULTILINE` mode also matches before a newline.  ``foo``
@@ -111,20 +117,31 @@ The special characters are:
    a single ``$`` in ``'foo\n'`` will find two (empty) matches: one just before
    the newline, and one at the end of the string.
 
+.. index:: single: * (asterisk); in regular expressions
+
 ``*``
    Causes the resulting RE to match 0 or more repetitions of the preceding RE, as
    many repetitions as are possible.  ``ab*`` will match 'a', 'ab', or 'a' followed
    by any number of 'b's.
 
+.. index:: single: + (plus); in regular expressions
+
 ``+``
    Causes the resulting RE to match 1 or more repetitions of the preceding RE.
    ``ab+`` will match 'a' followed by any non-zero number of 'b's; it will not
    match just 'a'.
 
+.. index:: single: ? (question mark); in regular expressions
+
 ``?``
    Causes the resulting RE to match 0 or 1 repetitions of the preceding RE.
    ``ab?`` will match either 'a' or 'ab'.
 
+.. index::
+   single: *?; in regular expressions
+   single: +?; in regular expressions
+   single: ??; in regular expressions
+
 ``*?``, ``+?``, ``??``
    The ``'*'``, ``'+'``, and ``'?'`` qualifiers are all :dfn:`greedy`; they match
    as much text as possible.  Sometimes this behaviour isn't desired; if the RE
@@ -134,6 +151,9 @@ The special characters are:
    characters as possible will be matched.  Using the RE ``<.*?>`` will match
    only ``'<a>'``.
 
+.. index::
+   single: {} (curly brackets); in regular expressions
+
 ``{m}``
    Specifies that exactly *m* copies of the previous RE should be matched; fewer
    matches cause the entire RE not to match.  For example, ``a{6}`` will match
@@ -155,6 +175,8 @@ The special characters are:
    6-character string ``'aaaaaa'``, ``a{3,5}`` will match 5 ``'a'`` characters,
    while ``a{3,5}?`` will only match 3 characters.
 
+.. index:: single: \ (backslash); in regular expressions
+
 ``\``
    Either escapes special characters (permitting you to match characters like
    ``'*'``, ``'?'``, and so forth), or signals a special sequence; special
@@ -168,12 +190,17 @@ The special characters are:
    is complicated and hard to understand, so it's highly recommended that you use
    raw strings for all but the simplest expressions.
 
+.. index::
+   single: [] (square brackets); in regular expressions
+
 ``[]``
    Used to indicate a set of characters.  In a set:
 
    * Characters can be listed individually, e.g. ``[amk]`` will match ``'a'``,
      ``'m'``, or ``'k'``.
 
+   .. index:: single: - (minus); in regular expressions
+
    * Ranges of characters can be indicated by giving two characters and separating
      them by a ``'-'``, for example ``[a-z]`` will match any lowercase ASCII letter,
      ``[0-5][0-9]`` will match all the two-digits numbers from ``00`` to ``59``, and
@@ -185,10 +212,14 @@ The special characters are:
      ``[(+*)]`` will match any of the literal characters ``'('``, ``'+'``,
      ``'*'``, or ``')'``.
 
+   .. index:: single: \ (backslash); in regular expressions
+
    * Character classes such as ``\w`` or ``\S`` (defined below) are also accepted
      inside a set, although the characters they match depends on whether
      :const:`ASCII` or :const:`LOCALE` mode is in force.
 
+   .. index:: single: ^ (caret); in regular expressions
+
    * Characters that are not within a range can be matched by :dfn:`complementing`
      the set.  If the first character of the set is ``'^'``, all the characters
      that are *not* in the set will be matched.  For example, ``[^5]`` will match
@@ -200,6 +231,11 @@ The special characters are:
      place it at the beginning of the set.  For example, both ``[()[\]{}]`` and
      ``[]()[{}]`` will both match a parenthesis.
 
+   .. .. index:: single: --; in regular expressions
+   .. .. index:: single: &&; in regular expressions
+   .. .. index:: single: ~~; in regular expressions
+   .. .. index:: single: ||; in regular expressions
+
    * Support of nested sets and set operations as in `Unicode Technical
      Standard #18`_ might be added in the future.  This would change the
      syntax, so to facilitate this change a :exc:`FutureWarning` will be raised
@@ -214,6 +250,8 @@ The special characters are:
       :exc:`FutureWarning` is raised if a character set contains constructs
       that will change semantically in the future.
 
+.. index:: single: | (vertical bar); in regular expressions
+
 ``|``
    ``A|B``, where *A* and *B* can be arbitrary REs, creates a regular expression that
    will match either *A* or *B*.  An arbitrary number of REs can be separated by the
@@ -225,6 +263,9 @@ The special characters are:
    greedy.  To match a literal ``'|'``, use ``\|``, or enclose it inside a
    character class, as in ``[|]``.
 
+.. index::
+   single: () (parentheses); in regular expressions
+
 ``(...)``
    Matches whatever regular expression is inside the parentheses, and indicates the
    start and end of a group; the contents of a group can be retrieved after a match
@@ -232,6 +273,8 @@ The special characters are:
    special sequence, described below.  To match the literals ``'('`` or ``')'``,
    use ``\(`` or ``\)``, or enclose them inside a character class: ``[(]``, ``[)]``.
 
+.. index:: single: (?; in regular expressions
+
 ``(?...)``
    This is an extension notation (a ``'?'`` following a ``'('`` is not meaningful
    otherwise).  The first character after the ``'?'`` determines what the meaning
@@ -253,6 +296,8 @@ The special characters are:
    :func:`re.compile` function.  Flags should be used first in the
    expression string.
 
+.. index:: single: (?:; in regular expressions
+
 ``(?:...)``
    A non-capturing version of regular parentheses.  Matches whatever regular
    expression is inside the parentheses, but the substring matched by the group
@@ -285,6 +330,8 @@ The special characters are:
    .. versionchanged:: 3.7
       The letters ``'a'``, ``'L'`` and ``'u'`` also can be used in a group.
 
+.. index:: single: (?P<; in regular expressions
+
 ``(?P<name>...)``
    Similar to regular parentheses, but the substring matched by the group is
    accessible via the symbolic group name *name*.  Group names must be valid
@@ -310,10 +357,14 @@ The special characters are:
    |                                       | * ``\1``                         |
    +---------------------------------------+----------------------------------+
 
+.. index:: single: (?P=; in regular expressions
+
 ``(?P=name)``
    A backreference to a named group; it matches whatever text was matched by the
    earlier group named *name*.
 
+.. index:: single: (?#; in regular expressions
+
 ``(?#...)``
    A comment; the contents of the parentheses are simply ignored.
 
@@ -322,11 +373,15 @@ The special characters are:
    called a :dfn:`lookahead assertion`.  For example, ``Isaac (?=Asimov)`` will match
    ``'Isaac '`` only if it's followed by ``'Asimov'``.
 
+.. index:: single: (?!; in regular expressions
+
 ``(?!...)``
    Matches if ``...`` doesn't match next.  This is a :dfn:`negative lookahead assertion`.
    For example, ``Isaac (?!Asimov)`` will match ``'Isaac '`` only if it's *not*
    followed by ``'Asimov'``.
 
+.. index:: single: (?<=; in regular expressions
+
 ``(?<=...)``
    Matches if the current position in the string is preceded by a match for ``...``
    that ends at the current position.  This is called a :dfn:`positive lookbehind
@@ -352,6 +407,8 @@ The special characters are:
    .. versionchanged:: 3.5
       Added support for group references of fixed length.
 
+.. index:: single: (?<!; in regular expressions
+
 ``(?<!...)``
    Matches if the current position in the string is not preceded by a match for
    ``...``.  This is called a :dfn:`negative lookbehind assertion`.  Similar to
@@ -373,6 +430,8 @@ If the ordinary character is not an ASCII digit or an ASCII letter, then the
 resulting RE will match the second character.  For example, ``\$`` matches the
 character ``'$'``.
 
+.. index:: single: \ (backslash); in regular expressions
+
 ``\number``
    Matches the contents of the group of the same number.  Groups are numbered
    starting from 1.  For example, ``(.+) \1`` matches ``'the the'`` or ``'55 55'``,
@@ -383,9 +442,13 @@ character ``'$'``.
    ``'['`` and ``']'`` of a character class, all numeric escapes are treated as
    characters.
 
+.. index:: single: \A; in regular expressions
+
 ``\A``
    Matches only at the start of the string.
 
+.. index:: single: \b; in regular expressions
+
 ``\b``
    Matches the empty string, but only at the beginning or end of a word.
    A word is defined as a sequence of word characters.  Note that formally,
@@ -400,6 +463,8 @@ character ``'$'``.
    Inside a character range, ``\b`` represents the backspace character, for
    compatibility with Python's string literals.
 
+.. index:: single: \B; in regular expressions
+
 ``\B``
    Matches the empty string, but only when it is *not* at the beginning or end
    of a word.  This means that ``r'py\B'`` matches ``'python'``, ``'py3'``,
@@ -409,6 +474,8 @@ character ``'$'``.
    be changed by using the :const:`ASCII` flag.  Word boundaries are
    determined by the current locale if the :const:`LOCALE` flag is used.
 
+.. index:: single: \d; in regular expressions
+
 ``\d``
    For Unicode (str) patterns:
       Matches any Unicode decimal digit (that is, any character in
@@ -419,11 +486,15 @@ character ``'$'``.
    For 8-bit (bytes) patterns:
       Matches any decimal digit; this is equivalent to ``[0-9]``.
 
+.. index:: single: \D; in regular expressions
+
 ``\D``
    Matches any character which is not a decimal digit. This is
    the opposite of ``\d``. If the :const:`ASCII` flag is used this
    becomes the equivalent of ``[^0-9]``.
 
+.. index:: single: \s; in regular expressions
+
 ``\s``
    For Unicode (str) patterns:
       Matches Unicode whitespace characters (which includes
@@ -436,11 +507,15 @@ character ``'$'``.
       Matches characters considered whitespace in the ASCII character set;
       this is equivalent to ``[ \t\n\r\f\v]``.
 
+.. index:: single: \S; in regular expressions
+
 ``\S``
    Matches any character which is not a whitespace character. This is
    the opposite of ``\s``. If the :const:`ASCII` flag is used this
    becomes the equivalent of ``[^ \t\n\r\f\v]``.
 
+.. index:: single: \w; in regular expressions
+
 ``\w``
    For Unicode (str) patterns:
       Matches Unicode word characters; this includes most characters
@@ -454,6 +529,8 @@ character ``'$'``.
       used, matches characters considered alphanumeric in the current locale
       and the underscore.
 
+.. index:: single: \W; in regular expressions
+
 ``\W``
    Matches any character which is not a word character. This is
    the opposite of ``\w``. If the :const:`ASCII` flag is used this
@@ -461,9 +538,25 @@ character ``'$'``.
    used, matches characters considered alphanumeric in the current locale
    and the underscore.
 
+.. index:: single: \Z; in regular expressions
+
 ``\Z``
    Matches only at the end of the string.
 
+.. index::
+   single: \a; in regular expressions
+   single: \b; in regular expressions
+   single: \f; in regular expressions
+   single: \n; in regular expressions
+   single: \N; in regular expressions
+   single: \r; in regular expressions
+   single: \t; in regular expressions
+   single: \u; in regular expressions
+   single: \U; in regular expressions
+   single: \v; in regular expressions
+   single: \x; in regular expressions
+   single: \\; in regular expressions
+
 Most of the standard escapes supported by Python string literals are also
 accepted by the regular expression parser::
 
@@ -620,6 +713,8 @@ form.
 .. data:: X
           VERBOSE
 
+   .. index:: single: # (hash); in regular expressions
+
    This flag allows you to write regular expressions that look nicer and are
    more readable by allowing you to visually separate logical sections of the
    pattern and add comments. Whitespace within the pattern is ignored, except
@@ -776,6 +871,8 @@ form.
    when not adjacent to a previous empty match, so ``sub('x*', '-', 'abxd')`` returns
    ``'-a-b--d-'``.
 
+   .. index:: single: \g; in regular expressions
+
    In string-type *repl* arguments, in addition to the character escapes and
    backreferences described above,
    ``\g<name>`` will use the substring matched by the group named ``name``, as
@@ -1510,38 +1607,40 @@ successive matches::
     import collections
     import re
 
-    Token = collections.namedtuple('Token', ['typ', 'value', 'line', 'column'])
+    Token = collections.namedtuple('Token', ['type', 'value', 'line', 'column'])
 
     def tokenize(code):
         keywords = {'IF', 'THEN', 'ENDIF', 'FOR', 'NEXT', 'GOSUB', 'RETURN'}
         token_specification = [
-            ('NUMBER',  r'\d+(\.\d*)?'),  # Integer or decimal number
-            ('ASSIGN',  r':='),           # Assignment operator
-            ('END',     r';'),            # Statement terminator
-            ('ID',      r'[A-Za-z]+'),    # Identifiers
-            ('OP',      r'[+\-*/]'),      # Arithmetic operators
-            ('NEWLINE', r'\n'),           # Line endings
-            ('SKIP',    r'[ \t]+'),       # Skip over spaces and tabs
-            ('MISMATCH',r'.'),            # Any other character
+            ('NUMBER',   r'\d+(\.\d*)?'),  # Integer or decimal number
+            ('ASSIGN',   r':='),           # Assignment operator
+            ('END',      r';'),            # Statement terminator
+            ('ID',       r'[A-Za-z]+'),    # Identifiers
+            ('OP',       r'[+\-*/]'),      # Arithmetic operators
+            ('NEWLINE',  r'\n'),           # Line endings
+            ('SKIP',     r'[ \t]+'),       # Skip over spaces and tabs
+            ('MISMATCH', r'.'),            # Any other character
         ]
         tok_regex = '|'.join('(?P<%s>%s)' % pair for pair in token_specification)
         line_num = 1
         line_start = 0
         for mo in re.finditer(tok_regex, code):
             kind = mo.lastgroup
-            value = mo.group(kind)
-            if kind == 'NEWLINE':
+            value = mo.group()
+            column = mo.start() - line_start
+            if kind == 'NUMBER':
+                value = float(value) if '.' in value else int(value)
+            elif kind == 'ID' and value in keywords:
+                kind = value
+            elif kind == 'NEWLINE':
                 line_start = mo.end()
                 line_num += 1
+                continue
             elif kind == 'SKIP':
-                pass
+                continue
             elif kind == 'MISMATCH':
                 raise RuntimeError(f'{value!r} unexpected on line {line_num}')
-            else:
-                if kind == 'ID' and value in keywords:
-                    kind = value
-                column = mo.start() - line_start
-                yield Token(kind, value, line_num, column)
+            yield Token(kind, value, line_num, column)
 
     statements = '''
         IF quantity THEN
@@ -1555,25 +1654,25 @@ successive matches::
 
 The tokenizer produces the following output::
 
-    Token(typ='IF', value='IF', line=2, column=4)
-    Token(typ='ID', value='quantity', line=2, column=7)
-    Token(typ='THEN', value='THEN', line=2, column=16)
-    Token(typ='ID', value='total', line=3, column=8)
-    Token(typ='ASSIGN', value=':=', line=3, column=14)
-    Token(typ='ID', value='total', line=3, column=17)
-    Token(typ='OP', value='+', line=3, column=23)
-    Token(typ='ID', value='price', line=3, column=25)
-    Token(typ='OP', value='*', line=3, column=31)
-    Token(typ='ID', value='quantity', line=3, column=33)
-    Token(typ='END', value=';', line=3, column=41)
-    Token(typ='ID', value='tax', line=4, column=8)
-    Token(typ='ASSIGN', value=':=', line=4, column=12)
-    Token(typ='ID', value='price', line=4, column=15)
-    Token(typ='OP', value='*', line=4, column=21)
-    Token(typ='NUMBER', value='0.05', line=4, column=23)
-    Token(typ='END', value=';', line=4, column=27)
-    Token(typ='ENDIF', value='ENDIF', line=5, column=4)
-    Token(typ='END', value=';', line=5, column=9)
+    Token(type='IF', value='IF', line=2, column=4)
+    Token(type='ID', value='quantity', line=2, column=7)
+    Token(type='THEN', value='THEN', line=2, column=16)
+    Token(type='ID', value='total', line=3, column=8)
+    Token(type='ASSIGN', value=':=', line=3, column=14)
+    Token(type='ID', value='total', line=3, column=17)
+    Token(type='OP', value='+', line=3, column=23)
+    Token(type='ID', value='price', line=3, column=25)
+    Token(type='OP', value='*', line=3, column=31)
+    Token(type='ID', value='quantity', line=3, column=33)
+    Token(type='END', value=';', line=3, column=41)
+    Token(type='ID', value='tax', line=4, column=8)
+    Token(type='ASSIGN', value=':=', line=4, column=12)
+    Token(type='ID', value='price', line=4, column=15)
+    Token(type='OP', value='*', line=4, column=21)
+    Token(type='NUMBER', value=0.05, line=4, column=23)
+    Token(type='END', value=';', line=4, column=27)
+    Token(type='ENDIF', value='ENDIF', line=5, column=4)
+    Token(type='END', value=';', line=5, column=9)
 
 
 .. [Frie09] Friedl, Jeffrey. Mastering Regular Expressions. 3rd ed., O'Reilly
index 5149bcf..1a0c1b0 100644 (file)
@@ -42,6 +42,9 @@ In addition to size-limiting tools, the module also provides a decorator for
 detecting recursive calls to :meth:`__repr__` and substituting a placeholder
 string instead.
 
+
+.. index:: single: ...; placeholder
+
 .. decorator:: recursive_repr(fillvalue="...")
 
    Decorator for :meth:`__repr__` methods to detect recursive calls within the
index bdfe5f6..bd49c87 100644 (file)
@@ -92,7 +92,7 @@ this module for those platforms.
    :exc:`PermissionError` when the user doesn't have ``CAP_SYS_RESOURCE`` for
    the process.
 
-   Availability: Linux 2.6.36 or later with glibc 2.13 or later
+   .. availability:: Linux 2.6.36 or later with glibc 2.13 or later.
 
    .. versionadded:: 3.4
 
@@ -178,7 +178,7 @@ platform.
 
    The number of bytes that can be allocated for POSIX message queues.
 
-   Availability: Linux 2.6.8 or later.
+   .. availability:: Linux 2.6.8 or later.
 
    .. versionadded:: 3.4
 
@@ -187,7 +187,7 @@ platform.
 
    The ceiling for the process's nice level (calculated as 20 - rlim_cur).
 
-   Availability: Linux 2.6.12 or later.
+   .. availability:: Linux 2.6.12 or later.
 
    .. versionadded:: 3.4
 
@@ -196,7 +196,7 @@ platform.
 
    The ceiling of the real-time priority.
 
-   Availability: Linux 2.6.12 or later.
+   .. availability:: Linux 2.6.12 or later.
 
    .. versionadded:: 3.4
 
@@ -206,7 +206,7 @@ platform.
    The time limit (in microseconds) on CPU time that a process can spend
    under real-time scheduling without making a blocking syscall.
 
-   Availability: Linux 2.6.25 or later.
+   .. availability:: Linux 2.6.25 or later.
 
    .. versionadded:: 3.4
 
@@ -215,7 +215,7 @@ platform.
 
    The number of signals which the process may queue.
 
-   Availability: Linux 2.6.8 or later.
+   .. availability:: Linux 2.6.8 or later.
 
    .. versionadded:: 3.4
 
@@ -225,7 +225,7 @@ platform.
    This limits the amount of network memory, and hence the amount of mbufs,
    that this user may hold at any time.
 
-   Availability: FreeBSD 9 or later.
+   .. availability:: FreeBSD 9 or later.
 
    .. versionadded:: 3.4
 
@@ -236,7 +236,7 @@ platform.
    This limit is enforced only if bit 1 of the vm.overcommit sysctl is set.
    Please see :manpage:`tuning(7)` for a complete description of this sysctl.
 
-   Availability: FreeBSD 9 or later.
+   .. availability:: FreeBSD 9 or later.
 
    .. versionadded:: 3.4
 
@@ -244,7 +244,7 @@ platform.
 
    The maximum number of pseudo-terminals created by this user id.
 
-   Availability: FreeBSD 9 or later.
+   .. availability:: FreeBSD 9 or later.
 
    .. versionadded:: 3.4
 
index 5ffb215..955ac2a 100644 (file)
@@ -171,7 +171,9 @@ The module defines the following:
    :func:`poll` or another interface in this module.  This doesn't apply
    to other kind of file-like objects such as sockets.
 
-   This value is guaranteed by POSIX to be at least 512.  Availability: Unix.
+   This value is guaranteed by POSIX to be at least 512.
+
+   .. availability:: Unix
 
    .. versionadded:: 3.2
 
index 1527deb..12e69a4 100644 (file)
@@ -166,7 +166,7 @@ Directory and files operations
 .. function:: copy2(src, dst, *, follow_symlinks=True)
 
    Identical to :func:`~shutil.copy` except that :func:`copy2`
-   also attempts to preserve all file metadata.
+   also attempts to preserve file metadata.
 
    When *follow_symlinks* is false, and *src* is a symbolic
    link, :func:`copy2` attempts to copy all metadata from the
@@ -323,7 +323,7 @@ Directory and files operations
 
    .. versionadded:: 3.3
 
-   Availability: Unix, Windows.
+   .. availability:: Unix, Windows.
 
 .. function:: chown(path, user=None, group=None)
 
@@ -334,7 +334,7 @@ Directory and files operations
 
    See also :func:`os.chown`, the underlying function.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
index fa90dff..148a59c 100644 (file)
@@ -106,7 +106,7 @@ The variables defined in the :mod:`signal` module are:
    The signal corresponding to the :kbd:`Ctrl+C` keystroke event. This signal can
    only be used with :func:`os.kill`.
 
-   Availability: Windows.
+   .. availability:: Windows.
 
    .. versionadded:: 3.2
 
@@ -116,7 +116,7 @@ The variables defined in the :mod:`signal` module are:
    The signal corresponding to the :kbd:`Ctrl+Break` keystroke event. This signal can
    only be used with :func:`os.kill`.
 
-   Availability: Windows.
+   .. availability:: Windows.
 
    .. versionadded:: 3.2
 
@@ -193,7 +193,9 @@ The :mod:`signal` module defines the following functions:
    then the number of seconds before any previously set alarm was to have been
    delivered. If *time* is zero, no alarm is scheduled, and any scheduled alarm is
    canceled.  If the return value is zero, no alarm is currently scheduled.  (See
-   the Unix man page :manpage:`alarm(2)`.) Availability: Unix.
+   the Unix man page :manpage:`alarm(2)`.)
+
+   .. availability:: Unix.
 
 
 .. function:: getsignal(signalnum)
@@ -234,8 +236,8 @@ The :mod:`signal` module defines the following functions:
    If *signalnum* is 0, then no signal is sent, but error checking is still
    performed; this can be used to check if the target thread is still running.
 
-   Availability: Unix (see the man page :manpage:`pthread_kill(3)` for further
-   information).
+   .. availability:: Unix (see the man page :manpage:`pthread_kill(3)` for further
+      information).
 
    See also :func:`os.kill`.
 
@@ -265,8 +267,8 @@ The :mod:`signal` module defines the following functions:
    For example, ``signal.pthread_sigmask(signal.SIG_BLOCK, [])`` reads the
    signal mask of the calling thread.
 
-   Availability: Unix. See the man page :manpage:`sigprocmask(3)` and
-   :manpage:`pthread_sigmask(3)` for further information.
+   .. availability:: Unix. See the man page :manpage:`sigprocmask(3)` and
+      :manpage:`pthread_sigmask(3)` for further information.
 
    See also :func:`pause`, :func:`sigpending` and :func:`sigwait`.
 
@@ -291,13 +293,16 @@ The :mod:`signal` module defines the following functions:
    The old values are returned as a tuple: (delay, interval).
 
    Attempting to pass an invalid interval timer will cause an
-   :exc:`ItimerError`.  Availability: Unix.
+   :exc:`ItimerError`.
+
+   .. availability:: Unix.
 
 
 .. function:: getitimer(which)
 
    Returns current value of a given interval timer specified by *which*.
-   Availability: Unix.
+
+   .. availability:: Unix.
 
 
 .. function:: set_wakeup_fd(fd, *, warn_on_full_buffer=True)
@@ -347,8 +352,10 @@ The :mod:`signal` module defines the following functions:
 
    Change system call restart behaviour: if *flag* is :const:`False`, system
    calls will be restarted when interrupted by signal *signalnum*, otherwise
-   system calls will be interrupted.  Returns nothing.  Availability: Unix (see
-   the man page :manpage:`siginterrupt(3)` for further information).
+   system calls will be interrupted.  Returns nothing.
+
+   .. availability:: Unix (see the man page :manpage:`siginterrupt(3)`
+      for further information).
 
    Note that installing a signal handler with :func:`signal` will reset the
    restart behaviour to interruptible by implicitly calling
@@ -387,8 +394,8 @@ The :mod:`signal` module defines the following functions:
    thread (i.e., the signals which have been raised while blocked).  Return the
    set of the pending signals.
 
-   Availability: Unix (see the man page :manpage:`sigpending(2)` for further
-   information).
+   .. availability:: Unix (see the man page :manpage:`sigpending(2)` for further
+      information).
 
    See also :func:`pause`, :func:`pthread_sigmask` and :func:`sigwait`.
 
@@ -401,8 +408,8 @@ The :mod:`signal` module defines the following functions:
    signals specified in the signal set *sigset*.  The function accepts the signal
    (removes it from the pending list of signals), and returns the signal number.
 
-   Availability: Unix (see the man page :manpage:`sigwait(3)` for further
-   information).
+   .. availability:: Unix (see the man page :manpage:`sigwait(3)` for further
+      information).
 
    See also :func:`pause`, :func:`pthread_sigmask`, :func:`sigpending`,
    :func:`sigwaitinfo` and :func:`sigtimedwait`.
@@ -426,8 +433,8 @@ The :mod:`signal` module defines the following functions:
    :attr:`si_errno`, :attr:`si_pid`, :attr:`si_uid`, :attr:`si_status`,
    :attr:`si_band`.
 
-   Availability: Unix (see the man page :manpage:`sigwaitinfo(2)` for further
-   information).
+   .. availability:: Unix (see the man page :manpage:`sigwaitinfo(2)` for further
+      information).
 
    See also :func:`pause`, :func:`sigwait` and :func:`sigtimedwait`.
 
@@ -445,8 +452,8 @@ The :mod:`signal` module defines the following functions:
    specifying a timeout. If *timeout* is specified as :const:`0`, a poll is
    performed. Returns :const:`None` if a timeout occurs.
 
-   Availability: Unix (see the man page :manpage:`sigtimedwait(2)` for further
-   information).
+   .. availability:: Unix (see the man page :manpage:`sigtimedwait(2)` for further
+      information).
 
    See also :func:`pause`, :func:`sigwait` and :func:`sigwaitinfo`.
 
index 5b5ed93..7974e20 100644 (file)
@@ -49,6 +49,10 @@ the key "include-system-site-packages" set to anything other than "false"
 (case-insensitive), the system-level prefixes will still also be
 searched for site-packages; otherwise they won't.
 
+.. index::
+   single: # (hash); comment
+   statement: import
+
 A path configuration file is a file whose name has the form :file:`{name}.pth`
 and exists in one of the four directories mentioned above; its contents are
 additional items (one per line) to be added to ``sys.path``.  Non-existing items
index 6fb0934..f59c184 100644 (file)
@@ -41,7 +41,7 @@ Protocol) and :rfc:`1869` (SMTP Service Extensions).
    the OS default behavior will be used.
 
    For normal use, you should only require the initialization/connect,
-   :meth:`sendmail`, and :meth:`~smtplib.quit` methods.
+   :meth:`sendmail`, and :meth:`SMTP.quit` methods.
    An example is included below.
 
    The :class:`SMTP` class supports the :keyword:`with` statement.  When used
index 32e7c5e..37b1577 100644 (file)
@@ -161,7 +161,7 @@ created.  Socket addresses are represented as follows:
 
   - *feat* and *mask* are unsigned 32bit integers.
 
-  Availability Linux 2.6.38, some algorithm types require more recent Kernels.
+  .. availability:: Linux 2.6.38, some algorithm types require more recent Kernels.
 
   .. versionadded:: 3.6
 
@@ -169,7 +169,7 @@ created.  Socket addresses are represented as follows:
   their hosts. The sockets are represented as a ``(CID, port)`` tuple
   where the context ID or CID and port are integers.
 
-  Availability: Linux >= 4.8 QEMU >= 2.8 ESX >= 4.0 ESX Workstation >= 6.5
+  .. availability:: Linux >= 4.8 QEMU >= 2.8 ESX >= 4.0 ESX Workstation >= 6.5.
 
   .. versionadded:: 3.7
 
@@ -306,7 +306,7 @@ Constants
       `Secure File Descriptor Handling <http://udrepper.livejournal.com/20407.html>`_
       for a more thorough explanation.
 
-   Availability: Linux >= 2.6.27.
+   .. availability:: Linux >= 2.6.27.
 
    .. versionadded:: 3.2
 
@@ -354,7 +354,7 @@ Constants
    Many constants of these forms, documented in the Linux documentation, are
    also defined in the socket module.
 
-   Availability: Linux >= 2.6.25.
+   .. availability:: Linux >= 2.6.25.
 
    .. versionadded:: 3.3
 
@@ -365,7 +365,7 @@ Constants
    Broadcast manager constants, documented in the Linux documentation, are also
    defined in the socket module.
 
-   Availability: Linux >= 2.6.25.
+   .. availability:: Linux >= 2.6.25.
 
    .. versionadded:: 3.4
 
@@ -377,7 +377,7 @@ Constants
 
    This constant is documented in the Linux documentation.
 
-   Availability: Linux >= 3.6.
+   .. availability:: Linux >= 3.6.
 
    .. versionadded:: 3.5
 
@@ -386,7 +386,7 @@ Constants
    CAN_ISOTP, in the CAN protocol family, is the ISO-TP (ISO 15765-2) protocol.
    ISO-TP constants, documented in the Linux documentation.
 
-   Availability: Linux >= 2.6.25
+   .. availability:: Linux >= 2.6.25.
 
    .. versionadded:: 3.7
 
@@ -398,7 +398,7 @@ Constants
    Many constants of these forms, documented in the Linux documentation, are
    also defined in the socket module.
 
-   Availability: Linux >= 2.2.
+   .. availability:: Linux >= 2.2.
 
 
 .. data:: AF_RDS
@@ -409,7 +409,7 @@ Constants
    Many constants of these forms, documented in the Linux documentation, are
    also defined in the socket module.
 
-   Availability: Linux >= 2.6.30.
+   .. availability:: Linux >= 2.6.30.
 
    .. versionadded:: 3.3
 
@@ -437,7 +437,7 @@ Constants
 
    Constants for Linux Kernel cryptography.
 
-   Availability: Linux >= 2.6.38.
+   .. availability:: Linux >= 2.6.38.
 
    .. versionadded:: 3.6
 
@@ -449,13 +449,13 @@ Constants
 
    Constants for Linux host/guest communication.
 
-   Availability: Linux >= 4.8.
+   .. availability:: Linux >= 4.8.
 
    .. versionadded:: 3.7
 
 .. data:: AF_LINK
 
-  Availability: BSD, OSX.
+  .. availability:: BSD, OSX.
 
   .. versionadded:: 3.4
 
@@ -604,7 +604,7 @@ The following functions all create :ref:`socket objects <socket-objects>`.
    Instantiate a socket from data obtained from the :meth:`socket.share`
    method.  The socket is assumed to be in blocking mode.
 
-   Availability: Windows.
+   .. availability:: Windows.
 
    .. versionadded:: 3.3
 
@@ -851,7 +851,7 @@ The :mod:`socket` module also offers various network-related services:
    both the value of *address_family* and the underlying implementation of
    :c:func:`inet_pton`.
 
-   Availability: Unix (maybe not all platforms), Windows.
+   .. availability:: Unix (maybe not all platforms), Windows.
 
    .. versionchanged:: 3.4
       Windows support added
@@ -871,7 +871,7 @@ The :mod:`socket` module also offers various network-related services:
    length for the specified address family, :exc:`ValueError` will be raised.
    :exc:`OSError` is raised for errors from the call to :func:`inet_ntop`.
 
-   Availability: Unix (maybe not all platforms), Windows.
+   .. availability:: Unix (maybe not all platforms), Windows.
 
    .. versionchanged:: 3.4
       Windows support added
@@ -897,7 +897,7 @@ The :mod:`socket` module also offers various network-related services:
    buffer.  Raises :exc:`OverflowError` if *length* is outside the
    permissible range of values.
 
-   Availability: most Unix platforms, possibly others.
+   .. availability:: most Unix platforms, possibly others.
 
    .. versionadded:: 3.3
 
@@ -918,7 +918,7 @@ The :mod:`socket` module also offers various network-related services:
    amount of ancillary data that can be received, since additional
    data may be able to fit into the padding area.
 
-   Availability: most Unix platforms, possibly others.
+   .. availability:: most Unix platforms, possibly others.
 
    .. versionadded:: 3.3
 
@@ -943,7 +943,7 @@ The :mod:`socket` module also offers various network-related services:
    Set the machine's hostname to *name*.  This will raise an
    :exc:`OSError` if you don't have enough rights.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -954,7 +954,7 @@ The :mod:`socket` module also offers various network-related services:
    (index int, name string) tuples.
    :exc:`OSError` if the system call fails.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -965,7 +965,7 @@ The :mod:`socket` module also offers various network-related services:
    interface name.
    :exc:`OSError` if no interface with the given name exists.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -976,7 +976,7 @@ The :mod:`socket` module also offers various network-related services:
    interface index number.
    :exc:`OSError` if no interface with the given index exists.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -1303,7 +1303,7 @@ to sockets.
                   fds.fromstring(cmsg_data[:len(cmsg_data) - (len(cmsg_data) % fds.itemsize)])
           return msg, list(fds)
 
-   Availability: most Unix platforms, possibly others.
+   .. availability:: most Unix platforms, possibly others.
 
    .. versionadded:: 3.3
 
@@ -1345,7 +1345,7 @@ to sockets.
       >>> [b1, b2, b3]
       [bytearray(b'Mary'), bytearray(b'01 had a 9'), bytearray(b'little lamb---')]
 
-   Availability: most Unix platforms, possibly others.
+   .. availability:: most Unix platforms, possibly others.
 
    .. versionadded:: 3.3
 
@@ -1449,7 +1449,7 @@ to sockets.
       def send_fds(sock, msg, fds):
           return sock.sendmsg([msg], [(socket.SOL_SOCKET, socket.SCM_RIGHTS, array.array("i", fds))])
 
-   Availability: most Unix platforms, possibly others.
+   .. availability:: most Unix platforms, possibly others.
 
    .. versionadded:: 3.3
 
@@ -1463,7 +1463,7 @@ to sockets.
    Specialized version of :meth:`~socket.sendmsg` for :const:`AF_ALG` socket.
    Set mode, IV, AEAD associated data length and flags for :const:`AF_ALG` socket.
 
-   Availability: Linux >= 2.6.38
+   .. availability:: Linux >= 2.6.38.
 
    .. versionadded:: 3.6
 
@@ -1564,7 +1564,7 @@ to sockets.
    Once this method has been called, it is safe to close the socket since
    the operating system has already duplicated it for the target process.
 
-   Availability: Windows.
+   .. availability:: Windows.
 
    .. versionadded:: 3.3
 
index e6aeee7..569bfce 100644 (file)
@@ -591,6 +591,9 @@ Cursor Objects
 
    A :class:`Cursor` instance has the following attributes and methods.
 
+   .. index:: single: ? (question mark); in SQL statements
+   .. index:: single: : (colon); in SQL statements
+
    .. method:: execute(sql[, parameters])
 
       Executes an SQL statement. The SQL statement may be parameterized (i. e.
index 911b28d..0835e9a 100644 (file)
@@ -328,7 +328,7 @@ Random generation
    See http://egd.sourceforge.net/ or http://prngd.sourceforge.net/ for sources
    of entropy-gathering daemons.
 
-   Availability: not available with LibreSSL and OpenSSL > 1.1.0
+   .. availability:: not available with LibreSSL and OpenSSL > 1.1.0.
 
 .. function:: RAND_add(bytes, entropy)
 
@@ -460,8 +460,8 @@ Certificate handling
    * :attr:`openssl_capath_env` - OpenSSL's environment key that points to a capath,
    * :attr:`openssl_capath` - hard coded path to a capath directory
 
-   Availability: LibreSSL ignores the environment vars
-   :attr:`openssl_cafile_env` and :attr:`openssl_capath_env`
+   .. availability:: LibreSSL ignores the environment vars
+     :attr:`openssl_cafile_env` and :attr:`openssl_capath_env`.
 
    .. versionadded:: 3.4
 
@@ -484,7 +484,7 @@ Certificate handling
       [(b'data...', 'x509_asn', {'1.3.6.1.5.5.7.3.1', '1.3.6.1.5.5.7.3.2'}),
        (b'data...', 'x509_asn', True)]
 
-   Availability: Windows.
+   .. availability:: Windows.
 
    .. versionadded:: 3.4
 
@@ -499,7 +499,7 @@ Certificate handling
    :const:`x509_asn` for X.509 ASN.1 data or :const:`pkcs_7_asn` for
    PKCS#7 ASN.1 data.
 
-   Availability: Windows.
+   .. availability:: Windows.
 
    .. versionadded:: 3.4
 
@@ -1610,7 +1610,7 @@ to speed up repeated connections from the same clients.
          'strength_bits': 128,
          'symmetric': 'aes-128-gcm'}]
 
-   Availability: OpenSSL 1.0.2+
+   .. availability:: OpenSSL 1.0.2+.
 
    .. versionadded:: 3.6
 
@@ -1941,7 +1941,7 @@ to speed up repeated connections from the same clients.
    .. note::
       With versions of OpenSSL older than 0.9.8m, it is only possible
       to set options, not to clear them.  Attempting to clear an option
-      (by resetting the corresponding bits) will raise a ``ValueError``.
+      (by resetting the corresponding bits) will raise a :exc:`ValueError`.
 
    .. versionchanged:: 3.6
       :attr:`SSLContext.options` returns :class:`Options` flags:
index b256312..c8f6904 100644 (file)
@@ -81,9 +81,9 @@ mode:
 
 .. function:: S_IMODE(mode)
 
-   Return the portion of the file's mode that can be set by :func:`os.chmod`\
-   ---that is, the file's permission bits, plus the sticky bit, set-group-id, and
-   set-user-id bits (on systems that support them).
+   Return the portion of the file's mode that can be set by
+   :func:`os.chmod`\ ---that is, the file's permission bits, plus the sticky
+   bit, set-group-id, and set-user-id bits (on systems that support them).
 
 
 .. function:: S_IFMT(mode)
index efa0a89..42c9282 100644 (file)
@@ -123,9 +123,9 @@ Comparisons
    pair: chaining; comparisons
    pair: operator; comparison
    operator: ==
-   operator: <
+   operator: < (less)
    operator: <=
-   operator: >
+   operator: > (greater)
    operator: >=
    operator: !=
    operator: is
@@ -248,12 +248,16 @@ and imaginary parts.
    builtin: int
    builtin: float
    builtin: complex
-   operator: +
-   operator: -
-   operator: *
-   operator: /
+   single: operator; + (plus)
+   single: + (plus); unary operator
+   single: + (plus); binary operator
+   single: operator; - (minus)
+   single: - (minus); unary operator
+   single: - (minus); binary operator
+   operator: * (asterisk)
+   operator: / (slash)
    operator: //
-   operator: %
+   operator: % (percent)
    operator: **
 
 Python fully supports mixed arithmetic: when a binary arithmetic operator has
@@ -389,12 +393,12 @@ Bitwise Operations on Integer Types
    pair: bitwise; operations
    pair: shifting; operations
    pair: masking; operations
-   operator: |
-   operator: ^
-   operator: &
+   operator: | (vertical bar)
+   operator: ^ (caret)
+   operator: & (ampersand)
    operator: <<
    operator: >>
-   operator: ~
+   operator: ~ (tilde)
 
 Bitwise operations only make sense for integers. The result of bitwise
 operations is calculated as though carried out in two's complement with an
@@ -441,7 +445,7 @@ Notes:
 (4)
    Performing these calculations with at least one extra sign extension bit in
    a finite two's complement representation (a working bit-width of
-   ``1 + max(x.bit_length(), y.bit_length()`` or more) is sufficient to get the
+   ``1 + max(x.bit_length(), y.bit_length())`` or more) is sufficient to get the
    same result as if there were an infinite number of sign bits.
 
 
@@ -2096,8 +2100,7 @@ expression support in the :mod:`re` module).
    single: string; interpolation, printf
    single: printf-style formatting
    single: sprintf-style formatting
-   single: % formatting
-   single: % interpolation
+   single: % (percent); printf-style formatting
 
 .. note::
 
@@ -2120,6 +2123,11 @@ object. [5]_  Otherwise, *values* must be a tuple with exactly the number of
 items specified by the format string, or a single mapping object (for example, a
 dictionary).
 
+.. index::
+   single: () (parentheses); in printf-style formatting
+   single: * (asterisk); in printf-style formatting
+   single: . (dot); in printf-style formatting
+
 A conversion specifier contains two or more characters and has the following
 components, which must occur in this order:
 
@@ -2158,6 +2166,12 @@ sequential parameter list).
 
 The conversion flag characters are:
 
+.. index::
+   single: # (hash); in printf-style formatting
+   single: - (minus); in printf-style formatting
+   single: + (plus); in printf-style formatting
+   single: space; in printf-style formatting
+
 +---------+---------------------------------------------------------------------+
 | Flag    | Meaning                                                             |
 +=========+=====================================================================+
@@ -3187,7 +3201,7 @@ place, and instead produce new objects.
 
    Return a copy of the sequence left filled with ASCII ``b'0'`` digits to
    make a sequence of length *width*. A leading sign prefix (``b'+'``/
-   ``b'-'`` is handled by inserting the padding *after* the sign character
+   ``b'-'``) is handled by inserting the padding *after* the sign character
    rather than before. For :class:`bytes` objects, the original sequence is
    returned if *width* is less than or equal to ``len(seq)``.
 
@@ -3210,18 +3224,17 @@ place, and instead produce new objects.
 ----------------------------------
 
 .. index::
-   single: formatting, bytes (%)
-   single: formatting, bytearray (%)
-   single: interpolation, bytes (%)
-   single: interpolation, bytearray (%)
+   single: formatting; bytes (%)
+   single: formatting; bytearray (%)
+   single: interpolation; bytes (%)
+   single: interpolation; bytearray (%)
    single: bytes; formatting
    single: bytearray; formatting
    single: bytes; interpolation
    single: bytearray; interpolation
    single: printf-style formatting
    single: sprintf-style formatting
-   single: % formatting
-   single: % interpolation
+   single: % (percent); printf-style formatting
 
 .. note::
 
@@ -3242,6 +3255,11 @@ object. [5]_  Otherwise, *values* must be a tuple with exactly the number of
 items specified by the format bytes object, or a single mapping object (for
 example, a dictionary).
 
+.. index::
+   single: () (parentheses); in printf-style formatting
+   single: * (asterisk); in printf-style formatting
+   single: . (dot); in printf-style formatting
+
 A conversion specifier contains two or more characters and has the following
 components, which must occur in this order:
 
@@ -3280,6 +3298,12 @@ sequential parameter list).
 
 The conversion flag characters are:
 
+.. index::
+   single: # (hash); in printf-style formatting
+   single: - (minus); in printf-style formatting
+   single: + (plus); in printf-style formatting
+   single: space; in printf-style formatting
+
 +---------+---------------------------------------------------------------------+
 | Flag    | Meaning                                                             |
 +=========+=====================================================================+
@@ -3743,7 +3767,7 @@ copying.
 
       ``nbytes == product(shape) * itemsize == len(m.tobytes())``. This is
       the amount of space in bytes that the array would use in a contiguous
-      representation. It is not necessarily equal to len(m)::
+      representation. It is not necessarily equal to ``len(m)``::
 
          >>> import array
          >>> a = array.array('i', [1,2,3,4,5])
@@ -4264,7 +4288,7 @@ pairs within braces, for example: ``{'jack': 4098, 'sjoerd': 4127}`` or ``{4098:
 
    .. versionchanged:: 3.7
       Dictionary order is guaranteed to be insertion order.  This behavior was
-      implementation detail of CPython from 3.6.
+      an implementation detail of CPython from 3.6.
 
 .. seealso::
    :class:`types.MappingProxyType` can be used to create a read-only view
@@ -4582,6 +4606,7 @@ supports no special operations.  There is exactly one null object, named
 It is written as ``None``.
 
 
+.. index:: single: ...; ellipsis literal
 .. _bltin-ellipsis-object:
 
 The Ellipsis Object
index 0fec3df..46b2bfc 100644 (file)
@@ -192,6 +192,13 @@ subclasses can define their own format string syntax).  The syntax is
 related to that of :ref:`formatted string literals <f-strings>`, but
 there are differences.
 
+.. index::
+   single: {} (curly brackets); in string formatting
+   single: . (dot); in string formatting
+   single: [] (square brackets); in string formatting
+   single: ! (exclamation); in string formatting
+   single: : (colon); in string formatting
+
 Format strings contain "replacement fields" surrounded by curly braces ``{}``.
 Anything that is not contained in braces is considered literal text, which is
 copied unchanged to the output.  If you need to include a brace character in the
@@ -323,6 +330,12 @@ affect the :func:`format` function.
 
 The meaning of the various alignment options is as follows:
 
+   .. index::
+      single: < (less); in string formatting
+      single: > (greater); in string formatting
+      single: = (equals); in string formatting
+      single: ^ (caret); in string formatting
+
    +---------+----------------------------------------------------------+
    | Option  | Meaning                                                  |
    +=========+==========================================================+
@@ -349,6 +362,11 @@ meaning in this case.
 The *sign* option is only valid for number types, and can be one of the
 following:
 
+   .. index::
+      single: + (plus); in string formatting
+      single: - (minus); in string formatting
+      single: space; in string formatting
+
    +---------+----------------------------------------------------------+
    | Option  | Meaning                                                  |
    +=========+==========================================================+
@@ -363,6 +381,8 @@ following:
    +---------+----------------------------------------------------------+
 
 
+.. index:: single: # (hash); in string formatting
+
 The ``'#'`` option causes the "alternate form" to be used for the
 conversion.  The alternate form is defined differently for different
 types.  This option is only valid for integer, float, complex and
@@ -375,6 +395,8 @@ decimal-point character appears in the result of these conversions
 only if a digit follows it. In addition, for ``'g'`` and ``'G'``
 conversions, trailing zeros are not removed from the result.
 
+.. index:: single: , (comma); in string formatting
+
 The ``','`` option signals the use of a comma for a thousands separator.
 For a locale aware separator, use the ``'n'`` integer presentation type
 instead.
@@ -382,6 +404,8 @@ instead.
 .. versionchanged:: 3.1
    Added the ``','`` option (see also :pep:`378`).
 
+.. index:: single: _ (underscore); in string formatting
+
 The ``'_'`` option signals the use of an underscore for a thousands
 separator for floating point presentation types and for integer
 presentation type ``'d'``.  For integer presentation types ``'b'``,
@@ -434,11 +458,11 @@ The available integer presentation types are:
    +---------+----------------------------------------------------------+
    | ``'o'`` | Octal format. Outputs the number in base 8.              |
    +---------+----------------------------------------------------------+
-   | ``'x'`` | Hex format. Outputs the number in base 16, using lower-  |
-   |         | case letters for the digits above 9.                     |
+   | ``'x'`` | Hex format. Outputs the number in base 16, using         |
+   |         | lower-case letters for the digits above 9.               |
    +---------+----------------------------------------------------------+
-   | ``'X'`` | Hex format. Outputs the number in base 16, using upper-  |
-   |         | case letters for the digits above 9.                     |
+   | ``'X'`` | Hex format. Outputs the number in base 16, using         |
+   |         | upper-case letters for the digits above 9.               |
    +---------+----------------------------------------------------------+
    | ``'n'`` | Number. This is the same as ``'d'``, except that it uses |
    |         | the current locale setting to insert the appropriate     |
@@ -527,7 +551,7 @@ addition of the ``{}`` and with ``:`` used instead of ``%``.
 For example, ``'%03.2f'`` can be translated to ``'{:03.2f}'``.
 
 The new format syntax also supports new and different options, shown in the
-follow examples.
+following examples.
 
 Accessing arguments by position::
 
@@ -668,6 +692,8 @@ formatting facilities in Python.  As an example of a library built on template
 strings for i18n, see the
 `flufl.i18n <http://flufli18n.readthedocs.io/en/latest/>`_ package.
 
+.. index:: single: $ (dollar); in template strings
+
 Template strings support ``$``-based substitutions, using the following rules:
 
 * ``$$`` is an escape; it is replaced with a single ``$``.
@@ -713,7 +739,7 @@ these rules.  The methods of :class:`Template` are:
       simply return ``$`` instead of raising :exc:`ValueError`.
 
       While other exceptions may still occur, this method is called "safe"
-      because substitutions always tries to return a usable string instead of
+      because it always tries to return a usable string instead of
       raising an exception.  In another sense, :meth:`safe_substitute` may be
       anything other than safe, since it will silently ignore malformed
       templates containing dangling delimiters, unmatched braces, or
index f10fbe4..df2416e 100644 (file)
@@ -117,6 +117,13 @@ By default, C types are represented in the machine's native format and byte
 order, and properly aligned by skipping pad bytes if necessary (according to the
 rules used by the C compiler).
 
+.. index::
+   single: @ (at); in struct format strings
+   single: = (equals); in struct format strings
+   single: < (less); in struct format strings
+   single: > (greater); in struct format strings
+   single: ! (exclamation); in struct format strings
+
 Alternatively, the first character of the format string can be used to indicate
 the byte order, size and alignment of the packed data, according to the
 following table:
@@ -240,6 +247,8 @@ platform-dependent.
 Notes:
 
 (1)
+   .. index:: single: ? (question mark); in struct format strings
+
    The ``'?'`` conversion code corresponds to the :c:type:`_Bool` type defined by
    C99. If this type is not available, it is simulated using a :c:type:`char`. In
    standard mode, it is always represented by one byte.
@@ -322,6 +331,8 @@ are used.  Note that for :func:`unpack`, the ``'p'`` format character consumes
 ``count`` bytes, but that the string returned can never contain more than 255
 bytes.
 
+.. index:: single: ? (question mark); in struct format strings
+
 For the ``'?'`` format character, the return value is either :const:`True` or
 :const:`False`. When packing, the truth value of the argument object is used.
 Either 0 or 1 in the native or standard bool representation will be packed, and
index 23beb52..4a10c7a 100644 (file)
@@ -39,7 +39,8 @@ compatibility with older versions, see the :ref:`call-function-trio` section.
 
 .. function:: run(args, *, stdin=None, input=None, stdout=None, stderr=None,\
                   capture_output=False, shell=False, cwd=None, timeout=None, \
-                  check=False, encoding=None, errors=None, text=None, env=None)
+                  check=False, encoding=None, errors=None, text=None, env=None, \
+                  universal_newlines=None)
 
    Run the command described by *args*.  Wait for command to complete, then
    return a :class:`CompletedProcess` instance.
@@ -334,7 +335,7 @@ functions.
 
 .. class:: Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None, \
                  stderr=None, preexec_fn=None, close_fds=True, shell=False, \
-                 cwd=None, env=None, universal_newlines=False, \
+                 cwd=None, env=None, universal_newlines=None, \
                  startupinfo=None, creationflags=0, restore_signals=True, \
                  start_new_session=False, pass_fds=(), *, \
                  encoding=None, errors=None, text=None)
@@ -1055,7 +1056,7 @@ calls these functions.
 
 .. function:: check_output(args, *, stdin=None, stderr=None, shell=False, \
                            cwd=None, encoding=None, errors=None, \
-                           universal_newlines=False, timeout=None)
+                           universal_newlines=None, timeout=None, text=None)
 
    Run command with arguments and return its output.
 
@@ -1101,6 +1102,10 @@ calls these functions.
    .. versionchanged:: 3.6
       *encoding* and *errors* were added.  See :func:`run` for details.
 
+   .. versionadded:: 3.7
+      *text* was added as a more readable alias for *universal_newlines*.
+
+
 .. _subprocess-replacements:
 
 Replacing Older Functions with the :mod:`subprocess` Module
@@ -1330,7 +1335,7 @@ handling consistency are valid for these functions.
       >>> subprocess.getstatusoutput('/bin/kill $$')
       (-15, '')
 
-   Availability: POSIX & Windows
+   .. availability:: POSIX & Windows.
 
    .. versionchanged:: 3.3.4
       Windows support was added.
@@ -1350,7 +1355,7 @@ handling consistency are valid for these functions.
       >>> subprocess.getoutput('ls /bin/ls')
       '/bin/ls'
 
-   Availability: POSIX & Windows
+   .. availability:: POSIX & Windows.
 
    .. versionchanged:: 3.3.4
       Windows support added
index a04d957..63861ab 100644 (file)
@@ -161,7 +161,9 @@ always available.
 
 .. data:: dllhandle
 
-   Integer specifying the handle of the Python DLL. Availability: Windows.
+   Integer specifying the handle of the Python DLL.
+
+   .. availability:: Windows.
 
 
 .. function:: displayhook(value)
@@ -457,7 +459,7 @@ always available.
 
    Return the build time API version of Android as an integer.
 
-   Availability: Android.
+   .. availability:: Android.
 
    .. versionadded:: 3.7
 
@@ -481,7 +483,9 @@ always available.
    Return the current value of the flags that are used for
    :c:func:`dlopen` calls.  Symbolic names for the flag values can be
    found in the :mod:`os` module (``RTLD_xxx`` constants, e.g.
-   :data:`os.RTLD_LAZY`).  Availability: Unix.
+   :data:`os.RTLD_LAZY`).
+
+   .. availability:: Unix.
 
 
 .. function:: getfilesystemencoding()
@@ -649,7 +653,7 @@ always available.
    is being emulated for the process. It is intended for use in logging rather
    than for feature detection.
 
-   Availability: Windows.
+   .. availability:: Windows.
 
    .. versionchanged:: 3.2
       Changed to a named tuple and added *service_pack_minor*,
@@ -679,7 +683,7 @@ always available.
 .. function:: get_coroutine_origin_tracking_depth()
 
    Get the current coroutine origin tracking depth, as set by
-   func:`set_coroutine_origin_tracking_depth`.
+   :func:`set_coroutine_origin_tracking_depth`.
 
    .. versionadded:: 3.7
 
@@ -1040,6 +1044,8 @@ always available.
    .. index::
       single: interpreter prompts
       single: prompts, interpreter
+      single: >>>; interpreter prompt
+      single: ...; interpreter prompt
 
    Strings specifying the primary and secondary prompt of the interpreter.  These
    are only defined if the interpreter is in interactive mode.  Their initial
@@ -1074,7 +1080,7 @@ always available.
    can be found in the :mod:`os` module (``RTLD_xxx`` constants, e.g.
    :data:`os.RTLD_LAZY`).
 
-   Availability: Unix.
+   .. availability:: Unix.
 
 .. function:: setprofile(profilefunc)
 
@@ -1320,7 +1326,7 @@ always available.
    This is equivalent to defining the :envvar:`PYTHONLEGACYWINDOWSFSENCODING`
    environment variable before launching Python.
 
-   Availability: Windows
+   .. availability:: Windows.
 
    .. versionadded:: 3.6
       See :pep:`529` for more details.
@@ -1466,7 +1472,9 @@ always available.
    stored as string resource 1000 in the Python DLL.  The value is normally the
    first three characters of :const:`version`.  It is provided in the :mod:`sys`
    module for informational purposes; modifying this value has no effect on the
-   registry keys used by Python. Availability: Windows.
+   registry keys used by Python.
+
+   .. availability:: Windows.
 
 
 .. data:: _xoptions
index 9f549fb..b5a1da8 100644 (file)
@@ -185,7 +185,7 @@ Other functions
 
    Windows will return one of:
 
-   - win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc)
+   - win-amd64 (64bit Windows on AMD64, aka x86_64, Intel64, and EM64T)
    - win32 (all others - specifically, sys.platform is returned)
 
    Mac OS X can return:
index 7693ecd..d75a87c 100644 (file)
@@ -51,8 +51,8 @@ The module defines the following functions:
 
 .. function:: tcsendbreak(fd, duration)
 
-   Send a break on file descriptor *fd*.  A zero *duration* sends a break for 0.25
-   --0.5 seconds; a nonzero *duration* has a system dependent meaning.
+   Send a break on file descriptor *fd*.  A zero *duration* sends a break for
+   0.25--0.5 seconds; a nonzero *duration* has a system dependent meaning.
 
 
 .. function:: tcdrain(fd)
index 438007d..d254466 100644 (file)
@@ -268,6 +268,8 @@ hyphenated words; only then will long words be broken if necessary, unless
       .. versionadded:: 3.4
 
 
+   .. index:: single: ...; placeholder
+
    .. attribute:: placeholder
 
       (default: ``' [...]'``) String that will appear at the end of the output
index e6185c5..a9d5268 100644 (file)
@@ -100,7 +100,8 @@ This module defines the following functions:
    memory page size - platform documentation should be referred to for more
    information (4 KiB pages are common; using multiples of 4096 for the stack size is
    the suggested approach in the absence of more specific information).
-   Availability: Windows, systems with POSIX threads.
+
+   .. availability:: Windows, systems with POSIX threads.
 
 
 This module also defines the following constant:
index 3eddc3f..c6a1f33 100644 (file)
@@ -170,8 +170,8 @@ Functions
       Passing an invalid or expired *thread_id* may result in
       undefined behavior, such as segmentation fault.
 
-   Availability: Unix (see the man page for :manpage:`pthread_getcpuclockid(3)` for
-   further information)
+   .. availability:: Unix (see the man page for :manpage:`pthread_getcpuclockid(3)` for
+      further information).
 
    .. versionadded:: 3.7
 
@@ -180,7 +180,7 @@ Functions
    Return the resolution (precision) of the specified clock *clk_id*.  Refer to
    :ref:`time-clock-id-constants` for a list of accepted values for *clk_id*.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -190,7 +190,7 @@ Functions
    Return the time of the specified clock *clk_id*.  Refer to
    :ref:`time-clock-id-constants` for a list of accepted values for *clk_id*.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -199,7 +199,7 @@ Functions
 
    Similar to :func:`clock_gettime` but return time as nanoseconds.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.7
 
@@ -209,7 +209,7 @@ Functions
    Set the time of the specified clock *clk_id*.  Currently,
    :data:`CLOCK_REALTIME` is the only accepted value for *clk_id*.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -218,7 +218,7 @@ Functions
 
    Similar to :func:`clock_settime` but set time with nanoseconds.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.7
 
@@ -369,6 +369,9 @@ Functions
       :pep:`475` for the rationale).
 
 
+.. index::
+   single: % (percent); datetime format
+
 .. function:: strftime(format[, t])
 
    Convert a tuple or :class:`struct_time` representing a time as returned by
@@ -500,6 +503,9 @@ Functions
    it is 3.
 
 
+.. index::
+   single: % (percent); datetime format
+
 .. function:: strptime(string[, format])
 
    Parse a string representing a time according to a format.  The return value
@@ -617,8 +623,8 @@ Functions
    returned value is undefined, so that only the difference between the results
    of consecutive calls in the same thread is valid.
 
-   Availability:  Windows, Linux, Unix systems supporting
-   ``CLOCK_THREAD_CPUTIME_ID``.
+   .. availability::  Windows, Linux, Unix systems supporting
+      ``CLOCK_THREAD_CPUTIME_ID``.
 
    .. versionadded:: 3.7
 
@@ -647,7 +653,7 @@ Functions
    nonzero if there is a time, past, present or future when daylight saving time
    applies).
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. note::
 
@@ -743,7 +749,7 @@ These constants are used as parameters for :func:`clock_getres` and
    have  discontinuities if the time is changed using ``settimeofday()`` or
    similar.
 
-   Availability: Linux 2.6.39 or later.
+   .. availability:: Linux 2.6.39 or later.
 
    .. versionadded:: 3.7
 
@@ -754,7 +760,7 @@ These constants are used as parameters for :func:`clock_getres` and
    hardware source, and may give close to nanosecond resolution.
    ``CLOCK_HIGHRES`` is the nonadjustable, high-resolution clock.
 
-   Availability: Solaris.
+   .. availability:: Solaris.
 
    .. versionadded:: 3.3
 
@@ -764,7 +770,7 @@ These constants are used as parameters for :func:`clock_getres` and
    Clock that cannot be set and represents monotonic time since some unspecified
    starting point.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -783,7 +789,7 @@ These constants are used as parameters for :func:`clock_getres` and
 
    High-resolution per-process timer from the CPU.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
@@ -792,7 +798,7 @@ These constants are used as parameters for :func:`clock_getres` and
 
    High-resolution per-process timer from the CPU.
 
-   Availability: FreeBSD, NetBSD 7 or later, OpenBSD.
+   .. availability:: FreeBSD, NetBSD 7 or later, OpenBSD.
 
    .. versionadded:: 3.7
 
@@ -812,7 +818,7 @@ These constants are used as parameters for :func:`clock_getres` and
    suspended, providing accurate uptime measurement, both absolute and
    interval.
 
-   Availability: FreeBSD, OpenBSD 5.5 or later.
+   .. availability:: FreeBSD, OpenBSD 5.5 or later.
 
    .. versionadded:: 3.7
 
@@ -825,7 +831,7 @@ The following constant is the only parameter that can be sent to
    System-wide real-time clock.  Setting this clock requires appropriate
    privileges.
 
-   Availability: Unix.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
index d9c1c35..60cf892 100644 (file)
@@ -205,6 +205,7 @@ A Simple Hello World Program
     class Application(tk.Frame):
         def __init__(self, master=None):
             super().__init__(master)
+            self.master = master
             self.pack()
             self.create_widgets()
 
@@ -215,7 +216,7 @@ A Simple Hello World Program
             self.hi_there.pack(side="top")
 
             self.quit = tk.Button(self, text="QUIT", fg="red",
-                                  command=root.destroy)
+                                  command=self.master.destroy)
             self.quit.pack(side="bottom")
 
         def say_hi(self):
@@ -262,6 +263,8 @@ To make a widget in Tk, the command is always of the form::
 *classCommand*
    denotes which kind of widget to make (a button, a label, a menu...)
 
+.. index:: single: . (dot); in Tkinter
+
 *newPathname*
    is the new name for this widget.  All names in Tk must be unique.  To help
    enforce this, widgets in Tk are named with *pathnames*, just like files in a
index 7ac3cac..462a6a5 100644 (file)
@@ -44,7 +44,11 @@ The module defines the following functions:
 
    * if *tb* is not ``None``, it prints a header ``Traceback (most recent
      call last):``
+
    * it prints the exception *etype* and *value* after the stack trace
+
+   .. index:: single: ^ (caret); marker
+
    * if *type(value)* is :exc:`SyntaxError` and *value* has the appropriate
      format, it prints the line where the syntax error occurred with a caret
      indicating the approximate position of the error.
index 595a244..175010b 100644 (file)
@@ -1309,7 +1309,7 @@ Using events
 
    :param fun: a function with two arguments which will be called with the
                coordinates of the clicked point on the canvas
-   :param num: number of the mouse-button, defaults to 1 (left mouse button)
+   :param btn: number of the mouse-button, defaults to 1 (left mouse button)
    :param add: ``True`` or ``False`` -- if ``True``, a new binding will be
                added, otherwise it will replace a former binding
 
@@ -1330,7 +1330,7 @@ Using events
 
    :param fun: a function with two arguments which will be called with the
                coordinates of the clicked point on the canvas
-   :param num: number of the mouse-button, defaults to 1 (left mouse button)
+   :param btn: number of the mouse-button, defaults to 1 (left mouse button)
    :param add: ``True`` or ``False`` -- if ``True``, a new binding will be
                added, otherwise it will replace a former binding
 
@@ -1354,7 +1354,7 @@ Using events
 
    :param fun: a function with two arguments which will be called with the
                coordinates of the clicked point on the canvas
-   :param num: number of the mouse-button, defaults to 1 (left mouse button)
+   :param btn: number of the mouse-button, defaults to 1 (left mouse button)
    :param add: ``True`` or ``False`` -- if ``True``, a new binding will be
                added, otherwise it will replace a former binding
 
@@ -1735,7 +1735,7 @@ Using screen events
 
    :param fun: a function with two arguments which will be called with the
                coordinates of the clicked point on the canvas
-   :param num: number of the mouse-button, defaults to 1 (left mouse button)
+   :param btn: number of the mouse-button, defaults to 1 (left mouse button)
    :param add: ``True`` or ``False`` -- if ``True``, a new binding will be
                added, otherwise it will replace a former binding
 
index 268adc0..0f37fc6 100644 (file)
@@ -689,6 +689,12 @@ The module defines the following classes, functions and decorators:
 
    .. versionadded:: 3.5.2
 
+.. class:: OrderedDict(collections.OrderedDict, MutableMapping[KT, VT])
+
+   A generic version of :class:`collections.OrderedDict`.
+
+   .. versionadded:: 3.7.2
+
 .. class:: Counter(collections.Counter, Dict[T, int])
 
    A generic version of :class:`collections.Counter`.
@@ -779,32 +785,25 @@ The module defines the following classes, functions and decorators:
 
    .. versionadded:: 3.5.2
 
-.. class:: io
-
-   Wrapper namespace for I/O stream types.
+.. class:: IO
+           TextIO
+           BinaryIO
 
-   This defines the generic type ``IO[AnyStr]`` and subclasses ``TextIO``
-   and ``BinaryIO``, deriving from ``IO[str]`` and ``IO[bytes]``,
-   respectively. These represent the types of I/O streams such as returned by
+   Generic type ``IO[AnyStr]`` and its subclasses ``TextIO(IO[str])``
+   and ``BinaryIO(IO[bytes])``
+   represent the types of I/O streams such as returned by
    :func:`open`.
 
-   These types are also accessible directly as ``typing.IO``,
-   ``typing.TextIO``, and ``typing.BinaryIO``.
+.. class:: Pattern
+           Match
 
-.. class:: re
-
-   Wrapper namespace for regular expression matching types.
-
-   This defines the type aliases ``Pattern`` and ``Match`` which
+   These type aliases
    correspond to the return types from :func:`re.compile` and
    :func:`re.match`.  These types (and the corresponding functions)
    are generic in ``AnyStr`` and can be made specific by writing
    ``Pattern[str]``, ``Pattern[bytes]``, ``Match[str]``, or
    ``Match[bytes]``.
 
-   These types are also accessible directly as ``typing.Pattern``
-   and ``typing.Match``.
-
 .. class:: NamedTuple
 
    Typed version of namedtuple.
@@ -899,7 +898,7 @@ The module defines the following classes, functions and decorators:
    non-``@overload``-decorated definition, while the latter is used at
    runtime but should be ignored by a type checker.  At runtime, calling
    a ``@overload``-decorated function directly will raise
-   ``NotImplementedError``. An example of overload that gives a more
+   :exc:`NotImplementedError`. An example of overload that gives a more
    precise type than can be expressed using a union or a type variable::
 
       @overload
index 65dee7c..00b9bc1 100644 (file)
@@ -153,6 +153,15 @@ You use the :data:`call` object to construct lists for comparing with
     >>> mock.mock_calls == expected
     True
 
+However, parameters to calls that return mocks are not recorded, which means it is not
+possible to track nested calls where the parameters used to create ancestors are important:
+
+    >>> m = Mock()
+    >>> m.factory(important=True).deliver()
+    <Mock name='mock.factory().deliver()' id='...'>
+    >>> m.mock_calls[-1] == call.factory(important=False).deliver()
+    True
+
 
 Setting Return Values and Attributes
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
index ef1b2e3..f1b2595 100644 (file)
@@ -680,6 +680,19 @@ the *new_callable* argument to :func:`patch`.
         unpacked as tuples to get at the individual arguments. See
         :ref:`calls as tuples <calls-as-tuples>`.
 
+        .. note::
+
+            The way :attr:`mock_calls` are recorded means that where nested
+            calls are made, the parameters of ancestor calls are not recorded
+            and so will always compare equal:
+
+                >>> mock = MagicMock()
+                >>> mock.top(a=3).bottom()
+                <MagicMock name='mock.top().bottom()' id='...'>
+                >>> mock.mock_calls
+                [call.top(a=3), call.top().bottom()]
+                >>> mock.mock_calls[-1] == call.top(a=-1).bottom()
+                True
 
     .. attribute:: __class__
 
@@ -2374,17 +2387,18 @@ Sealing mocks
 
 .. function:: seal(mock)
 
-    Seal will disable the creation of mock children by preventing getting or setting
-    of any new attribute on the sealed mock. The sealing process is performed recursively.
+    Seal will disable the automatic creation of mocks when accessing an attribute of
+    the mock being sealed or any of its attributes that are already mocks recursively.
 
-    If a mock instance is assigned to an attribute instead of being dynamically created
+    If a mock instance with a name or a spec is assigned to an attribute
     it won't be considered in the sealing chain. This allows one to prevent seal from
     fixing part of the mock object.
 
         >>> mock = Mock()
         >>> mock.submock.attribute1 = 2
-        >>> mock.not_submock = mock.Mock()
+        >>> mock.not_submock = mock.Mock(name="sample_name")
         >>> seal(mock)
+        >>> mock.new_attribute  # This will raise AttributeError.
         >>> mock.submock.attribute2  # This will raise AttributeError.
         >>> mock.not_submock.attribute2  # This won't raise.
 
index 83aee1b..774b087 100644 (file)
@@ -56,7 +56,7 @@ test runner
       Kent Beck's original paper on testing frameworks using the pattern shared
       by :mod:`unittest`.
 
-   `Nose <https://nose.readthedocs.io/>`_ and `py.test <https://docs.pytest.org/>`_
+   `Nose <https://nose.readthedocs.io/>`_ and `pytest <https://docs.pytest.org/>`_
       Third-party unittest frameworks with a lighter-weight syntax for writing
       tests.  For example, ``assert func(10) == 42``.
 
@@ -585,8 +585,8 @@ The following decorators implement test skipping and expected failures:
 
 .. decorator:: expectedFailure
 
-   Mark the test as an expected failure.  If the test fails when run, the test
-   is not counted as a failure.
+   Mark the test as an expected failure.  If the test fails it will be
+   considered a success.  If the test passes, it will be considered a failure.
 
 .. exception:: SkipTest(reason)
 
@@ -607,7 +607,7 @@ Distinguishing test iterations using subtests
 
 .. versionadded:: 3.4
 
-When some of your tests differ only by a some very small differences, for
+When there are very small differences among your tests, for
 instance some parameters, unittest allows you to distinguish them inside
 the body of a test method using the :meth:`~TestCase.subTest` context manager.
 
@@ -1161,7 +1161,7 @@ Test cases
       If *delta* is supplied instead of *places* then the difference
       between *first* and *second* must be less or equal to (or greater than) *delta*.
 
-      Supplying both *delta* and *places* raises a ``TypeError``.
+      Supplying both *delta* and *places* raises a :exc:`TypeError`.
 
       .. versionchanged:: 3.2
          :meth:`assertAlmostEqual` automatically considers almost equal objects
index 2d3488b..0c8f0f6 100644 (file)
@@ -137,7 +137,7 @@ or on combining URL components into a URL string.
       returning :const:`None`.
 
 
-.. function:: parse_qs(qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8', errors='replace')
+.. function:: parse_qs(qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8', errors='replace', max_num_fields=None)
 
    Parse a query string given as a string argument (data of type
    :mimetype:`application/x-www-form-urlencoded`).  Data are returned as a
@@ -158,16 +158,22 @@ or on combining URL components into a URL string.
    percent-encoded sequences into Unicode characters, as accepted by the
    :meth:`bytes.decode` method.
 
+   The optional argument *max_num_fields* is the maximum number of fields to
+   read. If set, then throws a :exc:`ValueError` if there are more than
+   *max_num_fields* fields read.
+
    Use the :func:`urllib.parse.urlencode` function (with the ``doseq``
    parameter set to ``True``) to convert such dictionaries into query
    strings.
 
-
    .. versionchanged:: 3.2
       Add *encoding* and *errors* parameters.
 
+   .. versionchanged:: 3.7.2
+      Added *max_num_fields* parameter.
 
-.. function:: parse_qsl(qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8', errors='replace')
+
+.. function:: parse_qsl(qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8', errors='replace', max_num_fields=None)
 
    Parse a query string given as a string argument (data of type
    :mimetype:`application/x-www-form-urlencoded`).  Data are returned as a list of
@@ -187,12 +193,18 @@ or on combining URL components into a URL string.
    percent-encoded sequences into Unicode characters, as accepted by the
    :meth:`bytes.decode` method.
 
+   The optional argument *max_num_fields* is the maximum number of fields to
+   read. If set, then throws a :exc:`ValueError` if there are more than
+   *max_num_fields* fields read.
+
    Use the :func:`urllib.parse.urlencode` function to convert such lists of pairs into
    query strings.
 
    .. versionchanged:: 3.2
       Add *encoding* and *errors* parameters.
 
+   .. versionchanged:: 3.7.2
+      Added *max_num_fields* parameter.
 
 .. function:: urlunparse(parts)
 
index cbbec0c..982f57f 100644 (file)
@@ -297,7 +297,7 @@ The following classes are provided:
    Cause requests to go through a proxy. If *proxies* is given, it must be a
    dictionary mapping protocol names to URLs of proxies. The default is to read
    the list of proxies from the environment variables
-   :envvar:`<protocol>_proxy`.  If no proxy environment variables are set, then
+   ``<protocol>_proxy``.  If no proxy environment variables are set, then
    in a Windows environment proxy settings are obtained from the registry's
    Internet Settings section, and in a Mac OS X environment proxy information
    is retrieved from the OS X System Configuration Framework.
index def926b..53f3d26 100644 (file)
@@ -135,7 +135,6 @@ creation according to their needs, the :class:`EnvBuilder` class.
     .. versionadded:: 3.6
        Added the ``prompt`` parameter
 
-
     Creators of third-party virtual environment tools will be free to use the
     provided ``EnvBuilder`` class as a base class.
 
@@ -182,16 +181,15 @@ creation according to their needs, the :class:`EnvBuilder` class.
 
     .. method:: setup_python(context)
 
-        Creates a copy of the Python executable (and, under Windows, DLLs) in
-        the environment. On a POSIX system, if a specific executable
-        ``python3.x`` was used, symlinks to ``python`` and ``python3`` will be
-        created pointing to that executable, unless files with those names
-        already exist.
+        Creates a copy of the Python executable in the environment on POSIX
+        systems. If a specific executable ``python3.x`` was used, symlinks to
+        ``python`` and ``python3`` will be created pointing to that executable,
+        unless files with those names already exist.
 
     .. method:: setup_scripts(context)
 
         Installs activation scripts appropriate to the platform into the virtual
-        environment.
+        environment. On Windows, also installs the ``python[w].exe`` scripts.
 
     .. method:: post_setup(context)
 
@@ -199,6 +197,11 @@ creation according to their needs, the :class:`EnvBuilder` class.
         implementations to pre-install packages in the virtual environment or
         perform other post-creation steps.
 
+    .. versionchanged:: 3.7.2
+       Windows now uses redirector scripts for ``python[w].exe`` instead of
+       copying the actual binaries, and so :meth:`setup_python` does nothing
+       unless running from a build in the source tree.
+
     In addition, :class:`EnvBuilder` provides this utility method that can be
     called from :meth:`setup_scripts` or :meth:`post_setup` in subclasses to
     assist in installing custom scripts into the virtual environment.
index 14f7896..e9c0261 100644 (file)
@@ -224,6 +224,9 @@ This module offers the following functions:
       See :ref:`above <exception-changed>`.
 
 
+.. index::
+   single: % (percent); environment variables expansion (Windows)
+
 .. function:: ExpandEnvironmentStrings(str)
 
    Expands environment variable placeholders ``%NAME%`` in strings like
index 40470e8..15b1cb0 100644 (file)
@@ -156,7 +156,7 @@ module documentation.  This section lists the differences between the API and
    encoding. Encoding this string in an encoding other than UTF-8 is
    likely incorrect, since UTF-8 is the default encoding of XML.
 
-.. method:: Node.toprettyxml(indent="", newl="", encoding="")
+.. method:: Node.toprettyxml(indent="\\t", newl="\\n", encoding=None)
 
    Return a pretty-printed version of the document. *indent* specifies the
    indentation string and defaults to a tabulator; *newl* specifies the string
index 9b8ba6b..fb86b6f 100644 (file)
@@ -75,8 +75,8 @@ decompression bomb         Safe             Safe              Safe             S
 2. :mod:`xml.dom.minidom` doesn't expand external entities and simply returns
    the unexpanded entity verbatim.
 3. :mod:`xmlrpclib` doesn't expand external entities and omits them.
-4. Since Python 3.8.0, external general entities are no longer processed by
-   default since Python.
+4. Since Python 3.7.1, external general entities are no longer processed by
+   default.
 
 
 billion laughs / exponential entity expansion
index 952090c..e3460e5 100644 (file)
@@ -40,7 +40,7 @@ The convenience functions are:
 
    Create and return a SAX :class:`~xml.sax.xmlreader.XMLReader` object.  The
    first parser found will
-   be used.  If *parser_list* is provided, it must be a sequence of strings which
+   be used.  If *parser_list* is provided, it must be a list of strings which
    name modules that have a function named :func:`create_parser`.  Modules listed
    in *parser_list* will be used before modules in the default list of parsers.
 
index c0f2a89..e1f8b9a 100644 (file)
@@ -363,7 +363,7 @@ ZipFile Objects
    Return the name of the first bad file, or else return ``None``.
 
    .. versionchanged:: 3.6
-      Calling :meth:`testfile` on a closed ZipFile will raise a
+      Calling :meth:`testzip` on a closed ZipFile will raise a
       :exc:`ValueError`.  Previously, a :exc:`RuntimeError` was raised.
 
 
@@ -380,13 +380,6 @@ ZipFile Objects
 
    .. note::
 
-      There is no official file name encoding for ZIP files. If you have unicode file
-      names, you must convert them to byte strings in your desired encoding before
-      passing them to :meth:`write`. WinZip interprets all file names as encoded in
-      CP437, also known as DOS Latin.
-
-   .. note::
-
       Archive names should be relative to the archive root, that is, they should not
       start with a path separator.
 
@@ -404,7 +397,9 @@ ZipFile Objects
 .. method:: ZipFile.writestr(zinfo_or_arcname, data, compress_type=None, \
                              compresslevel=None)
 
-   Write the string *data* to the archive; *zinfo_or_arcname* is either the file
+   Write a file into the archive.  The contents is *data*, which may be either
+   a :class:`str` or a :class:`bytes` instance; if it is a :class:`str`,
+   it is encoded as UTF-8 first.  *zinfo_or_arcname* is either the file
    name it will be given in the archive, or a :class:`ZipInfo` instance.  If it's
    an instance, at least the filename, date, and time must be given.  If it's a
    name, the date and time is set to the current date and time.
@@ -445,11 +440,11 @@ The following data attributes are also available:
 
 .. attribute:: ZipFile.comment
 
-   The comment text associated with the ZIP file.  If assigning a comment to a
+   The comment associated with the ZIP file as a :class:`bytes` object.
+   If assigning a comment to a
    :class:`ZipFile` instance created with mode ``'w'``, ``'x'`` or ``'a'``,
-   this should be a
-   string no longer than 65535 bytes.  Comments longer than this will be
-   truncated in the written archive when :meth:`close` is called.
+   it should be no longer than 65535 bytes.  Comments longer than this will be
+   truncated.
 
 
 .. _pyzipfile-objects:
@@ -606,13 +601,14 @@ Instances have the following methods and attributes:
 
 .. attribute:: ZipInfo.comment
 
-   Comment for the individual archive member.
+   Comment for the individual archive member as a :class:`bytes` object.
 
 
 .. attribute:: ZipInfo.extra
 
    Expansion field data.  The `PKZIP Application Note`_ contains
-   some comments on the internal structure of the data contained in this string.
+   some comments on the internal structure of the data contained in this
+   :class:`bytes` object.
 
 
 .. attribute:: ZipInfo.create_system
index 2ddb3e2..34b8bf7 100644 (file)
@@ -16,7 +16,7 @@ if not defined SPHINXBUILD (
         %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'; sys.exit(sphinx.main())"\r
+    set SPHINXBUILD=%PYTHON% -c "import sphinx.cmd.build, sys; sys.exit(sphinx.cmd.build.main())"\r
 )\r
 \r
 %PYTHON% -c "import python_docs_theme" > nul 2> nul\r
@@ -115,13 +115,16 @@ goto end
 :build\r
 if not exist "%BUILDDIR%" mkdir "%BUILDDIR%"\r
 \r
+rem PY_MISC_NEWS_DIR is also used by our Sphinx extension in tools/extensions/pyspecific.py\r
+if not defined PY_MISC_NEWS_DIR set PY_MISC_NEWS_DIR=%BUILDDIR%\%1\r
 if exist ..\Misc\NEWS (\r
-    echo.Copying Misc\NEWS to build\NEWS\r
-    copy ..\Misc\NEWS build\NEWS > nul\r
+    echo.Copying Misc\NEWS to %PY_MISC_NEWS_DIR%\NEWS\r
+    copy ..\Misc\NEWS "%PY_MISC_NEWS_DIR%\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
+        if not exist build mkdir build\r
+        %BLURB% merge -f "%PY_MISC_NEWS_DIR%\NEWS"\r
     ) else (\r
         echo.No Misc/NEWS file and Blurb is not available.\r
         exit /B 1\r
index aeb4ada..f98785a 100644 (file)
@@ -21,6 +21,7 @@ also syntactically compound statements.
 .. index::
    single: clause
    single: suite
+   single: ; (semicolon)
 
 A compound statement consists of one or more 'clauses.'  A clause consists of a
 header and a 'suite.'  The clause headers of a particular compound statement are
@@ -84,8 +85,7 @@ The :keyword:`if` statement
    statement: if
    keyword: elif
    keyword: else
-           keyword: elif
-           keyword: else
+   single: : (colon); compound statement
 
 The :keyword:`if` statement is used for conditional execution:
 
@@ -111,6 +111,7 @@ The :keyword:`while` statement
    keyword: else
    pair: loop; statement
    keyword: else
+   single: : (colon); compound statement
 
 The :keyword:`while` statement is used for repeated execution as long as an
 expression is true:
@@ -149,6 +150,7 @@ The :keyword:`for` statement
    keyword: else
    pair: target; list
    object: sequence
+   single: : (colon); compound statement
 
 The :keyword:`for` statement is used to iterate over the elements of a sequence
 (such as a string, tuple or list) or other iterable object:
@@ -229,7 +231,9 @@ The :keyword:`try` statement
    statement: try
    keyword: except
    keyword: finally
-.. index:: keyword: except
+   keyword: else
+   keyword: as
+   single: : (colon); compound statement
 
 The :keyword:`try` statement specifies exception handlers and/or cleanup code
 for a group of statements:
@@ -263,6 +267,8 @@ exception, the original search for a handler is canceled and a search starts for
 the new exception in the surrounding code and on the call stack (it is treated
 as if the entire :keyword:`try` statement raised the exception).
 
+.. index:: single: as; except clause
+
 When a matching except clause is found, the exception is assigned to the target
 specified after the :keyword:`as` keyword in that except clause, if present, and
 the except clause's suite is executed.  All except clauses must have an
@@ -308,9 +314,11 @@ from a function that handled an exception.
    statement: break
    statement: continue
 
-The optional :keyword:`else` clause is executed if and when control flows off
-the end of the :keyword:`try` clause. [#]_ Exceptions in the :keyword:`else`
-clause are not handled by the preceding :keyword:`except` clauses.
+The optional :keyword:`else` clause is executed if the control flow leaves the
+:keyword:`try` suite, no exception was raised, and no :keyword:`return`,
+:keyword:`continue`, or :keyword:`break` statement was executed.  Exceptions in
+the :keyword:`else` clause are not handled by the preceding :keyword:`except`
+clauses.
 
 .. index:: keyword: finally
 
@@ -374,8 +382,11 @@ The :keyword:`with` statement
 =============================
 
 .. index::
-    statement: with
-    single: as; with statement
+   statement: with
+   keyword: as
+   single: as; with statement
+   single: , (comma); with statement
+   single: : (colon); compound statement
 
 The :keyword:`with` statement is used to wrap the execution of a block with
 methods defined by a context manager (see section :ref:`context-managers`).
@@ -462,6 +473,9 @@ Function definitions
    object: function
    pair: function; name
    pair: name; binding
+   single: () (parentheses); function definition
+   single: , (comma); parameter list
+   single: : (colon); compound statement
 
 A function definition defines a user-defined function object (see section
 :ref:`types`):
@@ -491,7 +505,7 @@ The function definition does not execute the function body; this gets executed
 only when the function is called. [#]_
 
 .. index::
-  statement: @
+   single: @ (at); function definition
 
 A function definition may be wrapped by one or more :term:`decorator` expressions.
 Decorator expressions are evaluated when the function is defined, in the scope
@@ -514,6 +528,7 @@ except that the original function is not temporarily bound to the name ``func``.
 .. index::
    triple: default; parameter; value
    single: argument; function definition
+   single: = (equals); function definition
 
 When one or more :term:`parameters <parameter>` have the form *parameter* ``=``
 *expression*, the function is said to have "default parameter values."  For a
@@ -540,8 +555,8 @@ e.g.::
        return penguin
 
 .. index::
-  statement: *
-  statement: **
+   single: * (asterisk); function definition
+   single: **; function definition
 
 Function call semantics are described in more detail in section :ref:`calls`. A
 function call always assigns values to all parameters mentioned in the parameter
@@ -554,7 +569,10 @@ new empty mapping of the same type.  Parameters after "``*``" or
 "``*identifier``" are keyword-only parameters and may only be passed
 used keyword arguments.
 
-.. index:: pair: function; annotations
+.. index::
+   pair: function; annotations
+   single: ->; function annotations
+   single: : (colon); function annotations
 
 Parameters may have annotations of the form "``: expression``" following the
 parameter name.  Any parameter may have an annotation even those of the form
@@ -616,6 +634,9 @@ Class definitions
    pair: execution; frame
    single: inheritance
    single: docstring
+   single: () (parentheses); class definition
+   single: , (comma); expression list
+   single: : (colon); compound statement
 
 A class definition defines a class object (see section :ref:`types`):
 
@@ -654,6 +675,9 @@ the definition syntax.
 
 Class creation can be customized heavily using :ref:`metaclasses <metaclasses>`.
 
+.. index::
+   single: @ (at); class definition
+
 Classes can also be decorated: just like when decorating functions, ::
 
    @f1(arg)
@@ -680,8 +704,14 @@ can be used to create instance variables with different implementation details.
 
 .. seealso::
 
-   :pep:`3115` - Metaclasses in Python 3
+   :pep:`3115` - Metaclasses in Python 3000
+      The proposal that changed the declaration of metaclasses to the current
+      syntax, and the semantics for how classes with metaclasses are
+      constructed.
+
    :pep:`3129` - Class Decorators
+      The proposal that added class decorators.  Function and method decorators
+      were introduced in :pep:`318`.
 
 
 .. _async:
@@ -706,16 +736,16 @@ Coroutine function definition
    keyword: await
 
 Execution of Python coroutines can be suspended and resumed at many points
-(see :term:`coroutine`).  In the body of a coroutine, any ``await`` and
+(see :term:`coroutine`).  Inside the body of a coroutine function, ``await`` and
 ``async`` identifiers become reserved keywords; :keyword:`await` expressions,
 :keyword:`async for` and :keyword:`async with` can only be used in
-coroutine bodies.
+coroutine function bodies.
 
 Functions defined with ``async def`` syntax are always coroutine functions,
 even if they do not contain ``await`` or ``async`` keywords.
 
-It is a :exc:`SyntaxError` to use ``yield from`` expressions in
-``async def`` coroutines.
+It is a :exc:`SyntaxError` to use a ``yield from`` expression inside the body
+of a coroutine function.
 
 An example of a coroutine function::
 
@@ -764,8 +794,8 @@ Is semantically equivalent to::
 
 See also :meth:`__aiter__` and :meth:`__anext__` for details.
 
-It is a :exc:`SyntaxError` to use ``async for`` statement outside of an
-:keyword:`async def` function.
+It is a :exc:`SyntaxError` to use an ``async for`` statement outside the
+body of a coroutine function.
 
 
 .. index:: statement: async with
@@ -802,12 +832,14 @@ Is semantically equivalent to::
 
 See also :meth:`__aenter__` and :meth:`__aexit__` for details.
 
-It is a :exc:`SyntaxError` to use ``async with`` statement outside of an
-:keyword:`async def` function.
+It is a :exc:`SyntaxError` to use an ``async with`` statement outside the
+body of a coroutine function.
 
 .. seealso::
 
    :pep:`492` - Coroutines with async and await syntax
+      The proposal that made coroutines a proper standalone concept in Python,
+      and added supporting syntax.
 
 
 .. rubric:: Footnotes
@@ -816,10 +848,6 @@ It is a :exc:`SyntaxError` to use ``async with`` statement outside of an
    there is a :keyword:`finally` clause which happens to raise another
    exception. That new exception causes the old one to be lost.
 
-.. [#] Currently, control "flows off the end" except in the case of an exception
-   or the execution of a :keyword:`return`, :keyword:`continue`, or
-   :keyword:`break` statement.
-
 .. [#] A string literal appearing as the first statement in the function body is
    transformed into the function's ``__doc__`` attribute and therefore the
    function's :term:`docstring`.
index 735ecbf..e6fa8a0 100644 (file)
@@ -165,7 +165,9 @@ NotImplemented
 
 
 Ellipsis
-   .. index:: object: Ellipsis
+   .. index::
+      object: Ellipsis
+      single: ...; ellipsis literal
 
    This type has a single value.  There is a single object with this value. This
    object is accessed through the literal ``...`` or the built-in name
@@ -1450,8 +1452,8 @@ Basic customization
       dict insertion, O(n^2) complexity.  See
       http://www.ocert.org/advisories/ocert-2011-003.html for details.
 
-      Changing hash values affects the iteration order of dicts, sets and
-      other mappings.  Python has never made guarantees about this ordering
+      Changing hash values affects the iteration order of sets.
+      Python has never made guarantees about this ordering
       (and it typically varies between 32-bit and 64-bit builds).
 
       See also :envvar:`PYTHONHASHSEED`.
@@ -1578,7 +1580,7 @@ a module object to a subclass of :class:`types.ModuleType`. For example::
 
        def __setattr__(self, attr, value):
            print(f'Setting {attr}...')
-           setattr(self, attr, value)
+           super().__setattr__(attr, value)
 
    sys.modules[__name__].__class__ = VerboseModule
 
@@ -1831,8 +1833,9 @@ Metaclasses
 ^^^^^^^^^^^
 
 .. index::
-    single: metaclass
-    builtin: type
+   single: metaclass
+   builtin: type
+   single: = (equals); class definition
 
 By default, classes are constructed using :func:`type`. The class body is
 executed in a new namespace and the class name is bound locally to the
@@ -1996,46 +1999,14 @@ becomes the :attr:`~object.__dict__` attribute of the class object.
       Describes the implicit ``__class__`` closure reference
 
 
-Metaclass example
-^^^^^^^^^^^^^^^^^
+Uses for metaclasses
+^^^^^^^^^^^^^^^^^^^^
 
 The potential uses for metaclasses are boundless. Some ideas that have been
 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`
-to remember the order that class variables are defined::
-
-    class OrderedClass(type):
-
-        @classmethod
-        def __prepare__(metacls, name, bases, **kwds):
-            return collections.OrderedDict()
-
-        def __new__(cls, name, bases, namespace, **kwds):
-            result = type.__new__(cls, name, bases, dict(namespace))
-            result.members = tuple(namespace)
-            return result
-
-    class A(metaclass=OrderedClass):
-        def one(self): pass
-        def two(self): pass
-        def three(self): pass
-        def four(self): pass
-
-    >>> A.members
-    ('__module__', 'one', 'two', 'three', 'four')
-
-When the class definition for *A* gets executed, the process begins with
-calling the metaclass's :meth:`__prepare__` method which returns an empty
-:class:`collections.OrderedDict`.  That mapping records the methods and
-attributes of *A* as they are defined within the body of the class statement.
-Once those definitions are executed, the ordered dictionary is fully populated
-and the metaclass's :meth:`__new__` method gets invoked.  That method builds
-the new type and it saves the ordered dictionary keys in an attribute
-called ``members``.
-
 
 Customizing instance and subclass checks
 ----------------------------------------
@@ -2176,6 +2147,8 @@ through the container; for mappings, :meth:`__iter__` should be the same as
    .. versionadded:: 3.4
 
 
+.. index:: object: slice
+
 .. note::
 
    Slicing is done exclusively with the following three methods.  A call like ::
@@ -2191,8 +2164,6 @@ through the container; for mappings, :meth:`__iter__` should be the same as
 
 .. method:: object.__getitem__(self, key)
 
-   .. index:: object: slice
-
    Called to implement evaluation of ``self[key]``. For sequence types, the
    accepted keys should be integers and slice objects.  Note that the special
    interpretation of negative indexes (if the class wishes to emulate a sequence
@@ -2208,12 +2179,6 @@ through the container; for mappings, :meth:`__iter__` should be the same as
       indexes to allow proper detection of the end of the sequence.
 
 
-.. method:: object.__missing__(self, key)
-
-   Called by :class:`dict`\ .\ :meth:`__getitem__` to implement ``self[key]`` for dict subclasses
-   when key is not in the dictionary.
-
-
 .. method:: object.__setitem__(self, key, value)
 
    Called to implement assignment to ``self[key]``.  Same note as for
@@ -2232,6 +2197,12 @@ through the container; for mappings, :meth:`__iter__` should be the same as
    values as for the :meth:`__getitem__` method.
 
 
+.. method:: object.__missing__(self, key)
+
+   Called by :class:`dict`\ .\ :meth:`__getitem__` to implement ``self[key]`` for dict subclasses
+   when key is not in the dictionary.
+
+
 .. method:: object.__iter__(self)
 
    This method is called when an iterator is required for a container. This method
index d08abdf..1a69e97 100644 (file)
@@ -22,7 +22,7 @@ The following are blocks: a module, a function body, and a class definition.
 Each command typed interactively is a block.  A script file (a file given as
 standard input to the interpreter or specified as a command line argument to the
 interpreter) is a code block.  A script command (a command specified on the
-interpreter command line with the '**-c**' option) is a code block.  The string
+interpreter command line with the :option:`-c` option) is a code block.  The string
 argument passed to the built-in functions :func:`eval` and :func:`exec` is a
 code block.
 
@@ -52,7 +52,7 @@ Binding of names
 
 :dfn:`Names` refer to objects.  Names are introduced by name binding operations.
 
-.. index:: statement: from
+.. index:: single: from; import statement
 
 The following constructs bind names: formal parameters to functions,
 :keyword:`import` statements, class and function definitions (these bind the
index 4bacac3..3e7db90 100644 (file)
@@ -128,7 +128,9 @@ value.
 Parenthesized forms
 -------------------
 
-.. index:: single: parenthesized form
+.. index::
+   single: parenthesized form
+   single: () (parentheses); tuple display
 
 A parenthesized form is an optional expression list enclosed in parentheses:
 
@@ -146,8 +148,9 @@ immutable, the rules for literals apply (i.e., two occurrences of the empty
 tuple may or may not yield the same object).
 
 .. index::
-   single: comma
+   single: comma; tuple display
    pair: tuple; display
+   single: , (comma); tuple display
 
 Note that tuples are not formed by the parentheses, but rather by use of the
 comma operator.  The exception is the empty tuple, for which parentheses *are*
@@ -168,6 +171,11 @@ called "displays", each of them in two flavors:
 * they are computed via a set of looping and filtering instructions, called a
   :dfn:`comprehension`.
 
+.. index::
+   single: for; in comprehensions
+   single: if; in comprehensions
+   single: async for; in comprehensions
+
 Common syntax elements for comprehensions are:
 
 .. productionlist::
@@ -199,6 +207,9 @@ type, ``yield`` and ``yield from`` expressions are prohibited in the implicitly
 nested scope (in Python 3.7, such expressions emit :exc:`DeprecationWarning`
 when compiled, in Python 3.8+ they will emit :exc:`SyntaxError`).
 
+.. index::
+   single: await; in comprehensions
+
 Since Python 3.6, in an :keyword:`async def` function, an :keyword:`async for`
 clause may be used to iterate over a :term:`asynchronous iterator`.
 A comprehension in an :keyword:`async def` function may consist of either a
@@ -228,6 +239,8 @@ List displays
    pair: list; comprehensions
    pair: empty; list
    object: list
+   single: [] (square brackets); list expression
+   single: , (comma); expression list
 
 A list display is a possibly empty series of expressions enclosed in square
 brackets:
@@ -247,8 +260,11 @@ the list is constructed from the elements resulting from the comprehension.
 Set displays
 ------------
 
-.. index:: pair: set; display
-           object: set
+.. index::
+   pair: set; display
+   object: set
+   single: {} (curly brackets); set expression
+   single: , (comma); expression list
 
 A set display is denoted by curly braces and distinguishable from dictionary
 displays by the lack of colons separating keys and values:
@@ -271,9 +287,13 @@ dictionary.
 Dictionary displays
 -------------------
 
-.. index:: pair: dictionary; display
-           key, datum, key/datum pair
-           object: dictionary
+.. index::
+   pair: dictionary; display
+   key, datum, key/datum pair
+   object: dictionary
+   single: {} (curly brackets); dictionary expression
+   single: : (colon); in dictionary expressions
+   single: , (comma); in dictionary displays
 
 A dictionary display is a possibly empty series of key/datum pairs enclosed in
 curly braces:
@@ -292,7 +312,9 @@ used as a key into the dictionary to store the corresponding datum.  This means
 that you can specify the same key multiple times in the key/datum list, and the
 final dictionary's value for that key will be the last one given.
 
-.. index:: unpacking; dictionary, **; in dictionary displays
+.. index::
+   unpacking; dictionary
+   single: **; in dictionary displays
 
 A double asterisk ``**`` denotes :dfn:`dictionary unpacking`.
 Its operand must be a :term:`mapping`.  Each mapping item is added
@@ -322,8 +344,10 @@ prevails.
 Generator expressions
 ---------------------
 
-.. index:: pair: generator; expression
-           object: generator
+.. index::
+   pair: generator; expression
+   object: generator
+   single: () (parentheses); generator expression
 
 A generator expression is a compact generator notation in parentheses:
 
@@ -379,6 +403,7 @@ Yield expressions
 
 .. index::
    keyword: yield
+   keyword: from
    pair: yield; expression
    pair: generator; function
 
@@ -396,7 +421,7 @@ coroutine function to be an asynchronous generator. For example::
     def gen():  # defines a generator function
         yield 123
 
-    async def agen(): # defines an asynchronous generator function (PEP 525)
+    async def agen(): # defines an asynchronous generator function
         yield 123
 
 Due to their side effects on the containing scope, ``yield`` expressions
@@ -444,6 +469,9 @@ finalized (by reaching a zero reference count or by being garbage collected),
 the generator-iterator's :meth:`~generator.close` method will be called,
 allowing any pending :keyword:`finally` clauses to execute.
 
+.. index::
+   single: from; yield from expression
+
 When ``yield from <expr>`` is used, it treats the supplied expression as
 a subiterator. All values produced by that subiterator are passed directly
 to the caller of the current generator's methods. Any values passed in with
@@ -478,6 +506,10 @@ on the right hand side of an assignment statement.
       The proposal to introduce the :token:`yield_from` syntax, making delegation
       to sub-generators easy.
 
+   :pep:`525` - Asynchronous Generators
+      The proposal that expanded on :pep:`492` by adding generator capabilities to
+      coroutine functions.
+
 .. index:: object: generator
 .. _generator-methods:
 
@@ -723,7 +755,9 @@ syntax is:
 Attribute references
 --------------------
 
-.. index:: pair: attribute; reference
+.. index::
+   pair: attribute; reference
+   single: . (dot); attribute reference
 
 An attribute reference is a primary followed by a period and a name:
 
@@ -749,7 +783,9 @@ same attribute reference may yield different objects.
 Subscriptions
 -------------
 
-.. index:: single: subscription
+.. index::
+   single: subscription
+   single: [] (square brackets); subscription
 
 .. index::
    object: sequence
@@ -806,6 +842,8 @@ Slicings
 .. index::
    single: slicing
    single: slice
+   single: : (colon); slicing
+   single: , (comma); slicing
 
 .. index::
    object: sequence
@@ -855,6 +893,9 @@ substituting ``None`` for missing expressions.
    object: callable
    single: call
    single: argument; call semantics
+   single: () (parentheses); call
+   single: , (comma); argument list
+   single: = (equals); in function calls
 
 .. _calls:
 
@@ -931,7 +972,7 @@ and the argument values as corresponding values), or a (new) empty dictionary if
 there were no excess keyword arguments.
 
 .. index::
-   single: *; in function calls
+   single: * (asterisk); in function calls
    single: unpacking; in function calls
 
 If the syntax ``*expression`` appears in the function call, ``expression`` must
@@ -1037,6 +1078,7 @@ a class instance:
    if that method was called.
 
 
+.. index:: keyword: await
 .. _await:
 
 Await expression
@@ -1056,6 +1098,10 @@ Can only be used inside a :term:`coroutine function`.
 The power operator
 ==================
 
+.. index::
+   pair: power; operation
+   operator: **
+
 The power operator binds more tightly than unary operators on its left; it binds
 less tightly than unary operators on its right.  The syntax is:
 
@@ -1098,15 +1144,21 @@ All unary arithmetic and bitwise operations have the same priority:
 .. index::
    single: negation
    single: minus
+   single: operator; - (minus)
+   single: - (minus); unary operator
 
 The unary ``-`` (minus) operator yields the negation of its numeric argument.
 
-.. index:: single: plus
+.. index::
+   single: plus
+   single: operator; + (plus)
+   single: + (plus); unary operator
 
 The unary ``+`` (plus) operator yields its numeric argument unchanged.
 
-.. index:: single: inversion
-
+.. index::
+   single: inversion
+   operator: ~ (tilde)
 
 The unary ``~`` (invert) operator yields the bitwise inversion of its integer
 argument.  The bitwise inversion of ``x`` is defined as ``-(x+1)``.  It only
@@ -1136,7 +1188,9 @@ operators and one for additive operators:
          : `m_expr` "%" `u_expr`
    a_expr: `m_expr` | `a_expr` "+" `m_expr` | `a_expr` "-" `m_expr`
 
-.. index:: single: multiplication
+.. index::
+   single: multiplication
+   operator: * (asterisk)
 
 The ``*`` (multiplication) operator yields the product of its arguments.  The
 arguments must either both be numbers, or one argument must be an integer and
@@ -1146,7 +1200,7 @@ repetition is performed; a negative repetition factor yields an empty sequence.
 
 .. index::
    single: matrix multiplication
-   operator: @
+   operator: @ (at)
 
 The ``@`` (at) operator is intended to be used for matrix multiplication.  No
 builtin Python types implement this operator.
@@ -1156,6 +1210,8 @@ builtin Python types implement this operator.
 .. index::
    exception: ZeroDivisionError
    single: division
+   operator: / (slash)
+   operator: //
 
 The ``/`` (division) and ``//`` (floor division) operators yield the quotient of
 their arguments.  The numeric arguments are first converted to a common type.
@@ -1164,7 +1220,9 @@ integer; the result is that of mathematical division with the 'floor' function
 applied to the result.  Division by zero raises the :exc:`ZeroDivisionError`
 exception.
 
-.. index:: single: modulo
+.. index::
+   single: modulo
+   operator: % (percent)
 
 The ``%`` (modulo) operator yields the remainder from the division of the first
 argument by the second.  The numeric arguments are first converted to a common
@@ -1189,14 +1247,20 @@ The floor division operator, the modulo operator, and the :func:`divmod`
 function are not defined for complex numbers.  Instead, convert to a floating
 point number using the :func:`abs` function if appropriate.
 
-.. index:: single: addition
+.. index::
+   single: addition
+   single: operator; + (plus)
+   single: + (plus); binary operator
 
 The ``+`` (addition) operator yields the sum of its arguments.  The arguments
 must either both be numbers or both be sequences of the same type.  In the
 former case, the numbers are converted to a common type and then added together.
 In the latter case, the sequences are concatenated.
 
-.. index:: single: subtraction
+.. index::
+   single: subtraction
+   single: operator; - (minus)
+   single: - (minus); binary operator
 
 The ``-`` (subtraction) operator yields the difference of its arguments.  The
 numeric arguments are first converted to a common type.
@@ -1207,7 +1271,10 @@ numeric arguments are first converted to a common type.
 Shifting operations
 ===================
 
-.. index:: pair: shifting; operation
+.. index::
+   pair: shifting; operation
+   operator: <<
+   operator: >>
 
 The shifting operations have lower priority than the arithmetic operations:
 
@@ -1237,7 +1304,9 @@ Each of the three bitwise operations has a different priority level:
    xor_expr: `and_expr` | `xor_expr` "^" `and_expr`
    or_expr: `xor_expr` | `or_expr` "|" `xor_expr`
 
-.. index:: pair: bitwise; and
+.. index::
+   pair: bitwise; and
+   operator: & (ampersand)
 
 The ``&`` operator yields the bitwise AND of its arguments, which must be
 integers.
@@ -1245,6 +1314,7 @@ integers.
 .. index::
    pair: bitwise; xor
    pair: exclusive; or
+   operator: ^ (caret)
 
 The ``^`` operator yields the bitwise XOR (exclusive OR) of its arguments, which
 must be integers.
@@ -1252,6 +1322,7 @@ must be integers.
 .. index::
    pair: bitwise; or
    pair: inclusive; or
+   operator: | (vertical bar)
 
 The ``|`` operator yields the bitwise (inclusive) OR of its arguments, which
 must be integers.
@@ -1262,9 +1333,15 @@ must be integers.
 Comparisons
 ===========
 
-.. index:: single: comparison
-
-.. index:: pair: C; language
+.. index::
+   single: comparison
+   pair: C; language
+   operator: < (less)
+   operator: > (greater)
+   operator: <=
+   operator: >=
+   operator: ==
+   operator: !=
 
 Unlike C, all comparison operations in Python have the same priority, which is
 lower than that of any arithmetic, shifting or bitwise operation.  Also unlike
@@ -1567,7 +1644,7 @@ returned; otherwise, *y* is evaluated and the resulting value is returned.
 The expression ``x or y`` first evaluates *x*; if *x* is true, its value is
 returned; otherwise, *y* is evaluated and the resulting value is returned.
 
-(Note that neither :keyword:`and` nor :keyword:`or` restrict the value and type
+Note that neither :keyword:`and` nor :keyword:`or` restrict the value and type
 they return to ``False`` and ``True``, but rather return the last evaluated
 argument.  This is sometimes useful, e.g., if ``s`` is a string that should be
 replaced by a default value if it is empty, the expression ``s or 'foo'`` yields
@@ -1582,6 +1659,8 @@ Conditional expressions
 .. index::
    pair: conditional; expression
    pair: ternary; operator
+   single: if; conditional expression
+   single: else; conditional expression
 
 .. productionlist::
    conditional_expression: `or_test` ["if" `or_test` "else" `expression`]
@@ -1608,10 +1687,11 @@ Lambdas
    pair: lambda; expression
    pair: lambda; form
    pair: anonymous; function
+   single: : (colon); lambda expression
 
 .. productionlist::
-   lambda_expr: "lambda" [`parameter_list`]: `expression`
-   lambda_expr_nocond: "lambda" [`parameter_list`]: `expression_nocond`
+   lambda_expr: "lambda" [`parameter_list`] ":" `expression`
+   lambda_expr_nocond: "lambda" [`parameter_list`] ":" `expression_nocond`
 
 Lambda expressions (sometimes called lambda forms) are used to create anonymous
 functions. The expression ``lambda parameters: expression`` yields a function
@@ -1632,7 +1712,9 @@ annotations.
 Expression lists
 ================
 
-.. index:: pair: expression; list
+.. index::
+   pair: expression; list
+   single: , (comma); expression list
 
 .. productionlist::
    expression_list: `expression` ("," `expression`)* [","]
@@ -1649,7 +1731,7 @@ evaluated from left to right.
 
 .. index::
    pair: iterable; unpacking
-   single: *; in expression lists
+   single: * (asterisk); in expression lists
 
 An asterisk ``*`` denotes :dfn:`iterable unpacking`.  Its operand must be
 an :term:`iterable`.  The iterable is expanded into a sequence of items,
@@ -1694,7 +1776,8 @@ their suffixes::
 Operator precedence
 ===================
 
-.. index:: pair: operator; precedence
+.. index::
+   pair: operator; precedence
 
 The following table summarizes the operator precedence in Python, from lowest
 precedence (least binding) to highest precedence (most binding).  Operators in
@@ -1742,7 +1825,7 @@ precedence and have a left-to-right chaining feature as described in the
 +-----------------------------------------------+-------------------------------------+
 | ``**``                                        | Exponentiation [#]_                 |
 +-----------------------------------------------+-------------------------------------+
-| ``await`` ``x``                               | Await expression                    |
+| :keyword:`await` ``x``                        | Await expression                    |
 +-----------------------------------------------+-------------------------------------+
 | ``x[index]``, ``x[index:index]``,             | Subscription, slicing,              |
 | ``x(arguments...)``, ``x.attribute``          | call, attribute reference           |
index 44b5b81..d36d7d6 100644 (file)
@@ -127,8 +127,8 @@ Namespace packages
 ------------------
 
 .. index::
-    pair:: package; namespace
-    pair:: package; portion
+    pair: package; namespace
+    pair: package; portion
 
 A namespace package is a composite of various :term:`portions <portion>`,
 where each portion contributes a subpackage to the parent package.  Portions
@@ -951,7 +951,7 @@ In :ref:`the remaining cases <using-on-interface-options>`
 :mod:`__main__` does not correspond directly with an importable module:
 
 - interactive prompt
-- -c switch
+- :option:`-c` option
 - running from stdin
 - running directly from a source or bytecode file
 
index 84e8c78..b3c3215 100644 (file)
@@ -65,6 +65,7 @@ Comments
 --------
 
 .. index:: comment, hash character
+   single: # (hash); comment
 
 A comment starts with a hash character (``#``) that is not part of a string
 literal, and ends at the end of the physical line.  A comment signifies the end
@@ -78,6 +79,7 @@ Encoding declarations
 ---------------------
 
 .. index:: source character set, encoding declarations (source file)
+   single: # (hash); source encoding declaration
 
 If a comment in the first or second line of the Python script matches the
 regular expression ``coding[=:]\s*([-\w.]+)``, this comment is processed as an
@@ -349,6 +351,9 @@ exactly as written here:
    assert     del        global     not        with
    async      elif       if         or         yield
 
+.. index::
+   single: _, identifiers
+   single: __, identifiers
 .. _id-classes:
 
 Reserved classes of identifiers
@@ -395,13 +400,16 @@ Literals
 Literals are notations for constant values of some built-in types.
 
 
+.. index:: string literal, bytes literal, ASCII
+   single: ' (single quote); string literal
+   single: " (double quote); string literal
+   single: u'; string literal
+   single: u"; string literal
 .. _strings:
 
 String and Bytes literals
 -------------------------
 
-.. index:: string literal, bytes literal, ASCII
-
 String literals are described by the following lexical definitions:
 
 .. productionlist::
@@ -434,6 +442,8 @@ declaration; it is UTF-8 if no encoding declaration is given in the source file;
 see section :ref:`encodings`.
 
 .. index:: triple-quoted string, Unicode Consortium, raw string
+   single: """; string literal
+   single: '''; string literal
 
 In plain English: Both types of literals can be enclosed in matching single quotes
 (``'``) or double quotes (``"``).  They can also be enclosed in matching groups
@@ -442,11 +452,19 @@ of three single or double quotes (these are generally referred to as
 characters that otherwise have a special meaning, such as newline, backslash
 itself, or the quote character.
 
+.. index::
+   single: b'; bytes literal
+   single: b"; bytes literal
+
 Bytes literals are always prefixed with ``'b'`` or ``'B'``; they produce an
 instance of the :class:`bytes` type instead of the :class:`str` type.  They
 may only contain ASCII characters; bytes with a numeric value of 128 or greater
 must be expressed with escapes.
 
+.. index::
+   single: r'; raw string literal
+   single: r"; raw string literal
+
 Both string and bytes literals may optionally be prefixed with a letter ``'r'``
 or ``'R'``; such strings are called :dfn:`raw strings` and treat backslashes as
 literal characters.  As a result, in string literals, ``'\U'`` and ``'\u'``
@@ -463,6 +481,10 @@ is not supported.
    to simplify the maintenance of dual Python 2.x and 3.x codebases.
    See :pep:`414` for more information.
 
+.. index::
+   single: f'; formatted string literal
+   single: f"; formatted string literal
+
 A string literal with ``'f'`` or ``'F'`` in its prefix is a
 :dfn:`formatted string literal`; see :ref:`f-strings`.  The ``'f'`` may be
 combined with ``'r'``, but not with ``'b'`` or ``'u'``, therefore raw
@@ -473,6 +495,19 @@ retained), except that three unescaped quotes in a row terminate the literal.  (
 "quote" is the character used to open the literal, i.e. either ``'`` or ``"``.)
 
 .. index:: physical line, escape sequence, Standard C, C
+   single: \ (backslash); escape sequence
+   single: \\; escape sequence
+   single: \a; escape sequence
+   single: \b; escape sequence
+   single: \f; escape sequence
+   single: \n; escape sequence
+   single: \r; escape sequence
+   single: \t; escape sequence
+   single: \v; escape sequence
+   single: \x; escape sequence
+   single: \N; escape sequence
+   single: \u; escape sequence
+   single: \U; escape sequence
 
 Unless an ``'r'`` or ``'R'`` prefix is present, escape sequences in string and
 bytes literals are interpreted according to rules similar to those used by
@@ -601,6 +636,9 @@ and formatted string literals may be concatenated with plain string literals.
    single: string; formatted literal
    single: string; interpolated literal
    single: f-string
+   single: {} (curly brackets); in formatted string literal
+   single: ! (exclamation); in formatted string literal
+   single: : (colon); in formatted string literal
 .. _f-strings:
 
 Formatted string literals
@@ -735,6 +773,12 @@ actually an expression composed of the unary operator '``-``' and the literal
 ``1``.
 
 
+.. index::
+   single: 0b; integer literal
+   single: 0o; integer literal
+   single: 0x; integer literal
+   single: _ (underscore); in numeric literal
+
 .. _integers:
 
 Integer literals
@@ -775,6 +819,10 @@ Some examples of integer literals::
    Underscores are now allowed for grouping purposes in literals.
 
 
+.. index::
+   single: . (dot); in numeric literal
+   single: e; in numeric literal
+   single: _ (underscore); in numeric literal
 .. _floating:
 
 Floating point literals
@@ -803,6 +851,8 @@ Some examples of floating point literals::
    Underscores are now allowed for grouping purposes in literals.
 
 
+.. index::
+   single: j; in numeric literal
 .. _imaginary:
 
 Imaginary literals
index 47f7c7b..33ae051 100644 (file)
@@ -25,6 +25,7 @@ simple statements is:
               : | `break_stmt`
               : | `continue_stmt`
               : | `import_stmt`
+              : | `future_stmt`
               : | `global_stmt`
               : | `nonlocal_stmt`
 
@@ -71,7 +72,7 @@ Assignment statements
 =====================
 
 .. index::
-   single: =; assignment statement
+   single: = (equals); assignment statement
    pair: assignment; statement
    pair: binding; name
    pair: rebinding; name
@@ -112,17 +113,18 @@ unacceptable.  The rules observed by various types and the exceptions raised are
 given with the definition of the object types (see section :ref:`types`).
 
 .. index:: triple: target; list; assignment
+   single: , (comma); in target list
+   single: * (asterisk); in assignment target list
+   single: [] (square brackets); in assignment target list
+   single: () (parentheses); in assignment target list
 
 Assignment of an object to a target list, optionally enclosed in parentheses or
 square brackets, is recursively defined as follows.
 
-* If the target list is empty: The object must also be an empty iterable.
+* If the target list is a single target with no trailing comma,
+  optionally in parentheses, the object is assigned to that target.
 
-* If the target list is a single target in parentheses: The object is assigned
-  to that target.
-
-* If the target list is a comma-separated list of targets, or a single target
-  in square brackets: The object must be an iterable with the same number of
+* Else: The object must be an iterable with the same number of
   items as there are targets in the target list, and the items are assigned,
   from left to right, to the corresponding targets.
 
@@ -321,6 +323,7 @@ Annotated assignment statements
 .. index::
    pair: annotated; assignment
    single: statement; assignment, annotated
+   single: : (colon); annotated variable
 
 Annotation assignment is the combination, in a single statement,
 of a variable or attribute annotation and an optional assignment statement:
@@ -353,8 +356,15 @@ target, then the interpreter evaluates the target except for the last
 
 .. seealso::
 
-   :pep:`526` - Variable and attribute annotation syntax
+   :pep:`526` - Syntax for Variable Annotations
+      The proposal that added syntax for annotating the types of variables
+      (including class variables and instance variables), instead of expressing
+      them through comments.
+
    :pep:`484` - Type hints
+      The proposal that added the :mod:`typing` module to provide a standard
+      syntax for type annotations that can be used in static analysis tools and
+      IDEs.
 
 
 .. _assert:
@@ -365,6 +375,7 @@ The :keyword:`assert` statement
 .. index::
    statement: assert
    pair: debugging; assertions
+   single: , (comma); expression list
 
 Assert statements are a convenient way to insert debugging assertions into a
 program:
@@ -389,7 +400,7 @@ The extended form, ``assert expression1, expression2``, is equivalent to ::
 These equivalences assume that :const:`__debug__` and :exc:`AssertionError` refer to
 the built-in variables with those names.  In the current implementation, the
 built-in variable :const:`__debug__` is ``True`` under normal circumstances,
-``False`` when optimization is requested (command line option -O).  The current
+``False`` when optimization is requested (command line option :option:`-O`).  The current
 code generator emits no code for an assert statement when optimization is
 requested at compile time.  Note that it is unnecessary to include the source
 code for the expression that failed in the error message; it will be displayed
@@ -706,6 +717,9 @@ The :keyword:`import` statement
    single: module; importing
    pair: name; binding
    keyword: from
+   keyword: as
+   exception: ImportError
+   single: , (comma); import statement
 
 .. productionlist::
    import_stmt: "import" `module` ["as" `identifier`] ("," `module` ["as" `identifier`])*
@@ -755,8 +769,7 @@ available in the local namespace in one of three ways:
 
 .. index::
    pair: name; binding
-   keyword: from
-   exception: ImportError
+   single: from; import statement
 
 The :keyword:`from` form uses a slightly more complex process:
 
@@ -780,6 +793,8 @@ Examples::
    from foo.bar import baz    # foo.bar.baz imported and bound as baz
    from foo import attr       # foo imported and foo.attr bound as attr
 
+.. index:: single: * (asterisk); import statement
+
 If the list of identifiers is replaced by a star (``'*'``), all public
 names defined in the module are bound in the local namespace for the scope
 where the :keyword:`import` statement occurs.
@@ -825,7 +840,9 @@ determine dynamically the modules to be loaded.
 Future statements
 -----------------
 
-.. index:: pair: future; statement
+.. index::
+   pair: future; statement
+   single: __future__; future statement
 
 A :dfn:`future statement` is a directive to the compiler that a particular
 module should be compiled using syntax or semantics that will be available in a
@@ -912,6 +929,7 @@ The :keyword:`global` statement
 .. index::
    statement: global
    triple: global; name; binding
+   single: , (comma); identifier list
 
 .. productionlist::
    global_stmt: "global" `identifier` ("," `identifier`)*
@@ -956,6 +974,7 @@ The :keyword:`nonlocal` statement
 =================================
 
 .. index:: statement: nonlocal
+   single: , (comma); identifier list
 
 .. productionlist::
    nonlocal_stmt: "nonlocal" `identifier` ("," `identifier`)*
index 6f2e357..e999971 100644 (file)
@@ -8,6 +8,8 @@ https://bugs.python.org/issue32174
 import re
 from html.entities import codepoint2name
 
+from sphinx.util.logging import getLogger
+
 # escape the characters which codepoint > 0x7F
 def _process(string):
     def escape(matchobj):
@@ -23,7 +25,7 @@ def _process(string):
 
 def escape_for_chm(app, pagename, templatename, context, doctree):
     # only works for .chm output
-    if not hasattr(app.builder, 'name') or app.builder.name != 'htmlhelp':
+    if getattr(app.builder, 'name', '') != 'htmlhelp':
         return
 
     # escape the `body` part to 7-bit ASCII
@@ -31,9 +33,25 @@ def escape_for_chm(app, pagename, templatename, context, doctree):
     if body is not None:
         context['body'] = _process(body)
 
+def fixup_keywords(app, exception):
+    # only works for .chm output
+    if getattr(app.builder, 'name', '') != 'htmlhelp' or exception:
+        return
+
+    getLogger(__name__).info('fixing HTML escapes in keywords file...')
+    outdir = app.builder.outdir
+    outname = app.builder.config.htmlhelp_basename
+    with app.builder.open_file(outdir, outname + '.hhk', 'r') as f:
+        index = f.read()
+    with app.builder.open_file(outdir, outname + '.hhk', 'w') as f:
+        f.write(index.replace('&#x27;', '&#39;'))
+
 def setup(app):
     # `html-page-context` event emitted when the HTML builder has
     # created a context dictionary to render a template with.
     app.connect('html-page-context', escape_for_chm)
+    # `build-finished` event emitted when all the files have been
+    # output.
+    app.connect('build-finished', fixup_keywords)
 
     return {'version': '1.0', 'parallel_read_safe': True}
index 90d7cea..466e84c 100644 (file)
@@ -11,7 +11,7 @@
 
 import re
 import io
-from os import path
+from os import getenv, path
 from time import asctime
 from pprint import pformat
 from docutils.io import StringOutput
@@ -131,6 +131,26 @@ class ImplementationDetail(Directive):
         return [pnode]
 
 
+# Support for documenting platform availability
+
+class Availability(Directive):
+
+    has_content = False
+    required_arguments = 1
+    optional_arguments = 0
+    final_argument_whitespace = True
+
+    def run(self):
+        availability_ref = ':ref:`Availability <availability>`: '
+        pnode = nodes.paragraph(availability_ref + self.arguments[0],
+                                classes=["availability"],)
+        n, m = self.state.inline_text(availability_ref, self.lineno)
+        pnode.extend(n + m)
+        n, m = self.state.inline_text(self.arguments[0], self.lineno)
+        pnode.extend(n + m)
+        return [pnode]
+
+
 # Support for documenting decorators
 
 class PyDecoratorMixin(object):
@@ -272,7 +292,9 @@ class MiscNews(Directive):
         fname = self.arguments[0]
         source = self.state_machine.input_lines.source(
             self.lineno - self.state_machine.input_offset - 1)
-        source_dir = path.dirname(path.abspath(source))
+        source_dir = getenv('PY_MISC_NEWS_DIR')
+        if not source_dir:
+            source_dir = path.dirname(path.abspath(source))
         fpath = path.join(source_dir, fname)
         self.state.document.settings.record_dependencies.add(fpath)
         try:
@@ -401,14 +423,13 @@ def setup(app):
     app.add_role('issue', issue_role)
     app.add_role('source', source_role)
     app.add_directive('impl-detail', ImplementationDetail)
+    app.add_directive('availability', Availability)
     app.add_directive('deprecated-removed', DeprecatedRemoved)
     app.add_builder(PydocTopicsBuilder)
     app.add_builder(suspicious.CheckSuspiciousMarkupBuilder)
-    app.add_description_unit('opcode', 'opcode', '%s (opcode)',
-                             parse_opcode_signature)
-    app.add_description_unit('pdbcommand', 'pdbcmd', '%s (pdb command)',
-                             parse_pdb_command)
-    app.add_description_unit('2to3fixer', '2to3fixer', '%s (2to3 fixer)')
+    app.add_object_type('opcode', 'opcode', '%s (opcode)', parse_opcode_signature)
+    app.add_object_type('pdbcommand', 'pdbcmd', '%s (pdb command)', parse_pdb_command)
+    app.add_object_type('2to3fixer', '2to3fixer', '%s (2to3 fixer)')
     app.add_directive_to_domain('py', 'decorator', PyDecoratorFunction)
     app.add_directive_to_domain('py', 'decoratormethod', PyDecoratorMethod)
     app.add_directive_to_domain('py', 'coroutinefunction', PyCoroutineFunction)
index 2c1816e..a3024d6 100755 (executable)
@@ -27,17 +27,18 @@ directives = [
     'table', 'target-notes', 'tip', 'title', 'topic', 'unicode', 'warning',
     # Sphinx and Python docs custom ones
     'acks', 'attribute', 'autoattribute', 'autoclass', 'autodata',
-    'autoexception', 'autofunction', 'automethod', 'automodule', 'centered',
-    'cfunction', 'class', 'classmethod', 'cmacro', 'cmdoption', 'cmember',
-    'code-block', 'confval', 'cssclass', 'ctype', 'currentmodule', 'cvar',
-    'data', 'decorator', 'decoratormethod', 'deprecated-removed',
-    'deprecated(?!-removed)', 'describe', 'directive', 'doctest', 'envvar',
-    'event', 'exception', 'function', 'glossary', 'highlight', 'highlightlang',
-    'impl-detail', 'index', 'literalinclude', 'method', 'miscnews', 'module',
-    'moduleauthor', 'opcode', 'pdbcommand', 'productionlist',
-    'program', 'role', 'sectionauthor', 'seealso', 'sourcecode', 'staticmethod',
-    'tabularcolumns', 'testcode', 'testoutput', 'testsetup', 'toctree', 'todo',
-    'todolist', 'versionadded', 'versionchanged'
+    'autoexception', 'autofunction', 'automethod', 'automodule',
+    'availability', 'centered', 'cfunction', 'class', 'classmethod', 'cmacro',
+    'cmdoption', 'cmember', 'code-block', 'confval', 'cssclass', 'ctype',
+    'currentmodule', 'cvar', 'data', 'decorator', 'decoratormethod',
+    'deprecated-removed', 'deprecated(?!-removed)', 'describe', 'directive',
+    'doctest', 'envvar', 'event', 'exception', 'function', 'glossary',
+    'highlight', 'highlightlang', 'impl-detail', 'index', 'literalinclude',
+    'method', 'miscnews', 'module', 'moduleauthor', 'opcode', 'pdbcommand',
+    'productionlist', 'program', 'role', 'sectionauthor', 'seealso',
+    'sourcecode', 'staticmethod', 'tabularcolumns', 'testcode', 'testoutput',
+    'testsetup', 'toctree', 'todo', 'todolist', 'versionadded',
+    'versionchanged'
 ]
 
 all_directives = '(' + '|'.join(directives) + ')'
index d885ff2..20dad93 100644 (file)
       else
         buf.push('<option value="' + language + '">' + title + '</option>');
     });
+    if (!(current_language in all_languages)) {
+        // In case we're browsing a language that is not yet in all_languages.
+        buf.push('<option value="' + current_language + '" selected="selected">' +
+                 current_language + '</option>');
+        all_languages[current_language] = current_language;
+    }
     buf.push('</select>');
     return buf.join('');
   }
index 33cd48f..30271bb 100644 (file)
@@ -14,7 +14,7 @@ faq/programming,,:chr,">=4.0) or 1+f(xc,yc,x*x-y*y+xc,2.0*x*y+yc,k-1,f):f(xc,yc,
 faq/programming,,::,for x in sequence[::-1]:
 faq/programming,,:reduce,"print((lambda Ru,Ro,Iu,Io,IM,Sx,Sy:reduce(lambda x,y:x+y,map(lambda y,"
 faq/programming,,:reduce,"Sx=Sx,Sy=Sy:reduce(lambda x,y:x+y,map(lambda x,xc=Ru,yc=yc,Ru=Ru,Ro=Ro,"
-faq/windows,,:bd8afb90ebf2,"Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:55:48) [MSC v.1600 32 bit (Intel)] on win32"
+faq/windows,,:d48eceb,"Python 3.6.4 (v3.6.4:d48eceb, Dec 19 2017, 06:04:45) [MSC v.1900 32 bit (Intel)] on win32"
 howto/cporting,,:encode,"if (!PyArg_ParseTuple(args, ""O:encode_object"", &myobj))"
 howto/cporting,,:say,"if (!PyArg_ParseTuple(args, ""U:say_hello"", &name))"
 howto/curses,,:black,"colors when it activates color mode.  They are: 0:black, 1:red,"
@@ -180,9 +180,17 @@ library/pathlib,,:Program,>>> PureWindowsPath('c:Program Files/').anchor
 library/pdb,,:lineno,filename:lineno
 library/pickle,,:memory,"conn = sqlite3.connect("":memory:"")"
 library/posix,,`,"CFLAGS=""`getconf LFS_CFLAGS`"" OPT=""-g -O2 $CFLAGS"""
-library/pprint,,::,"'Programming Language :: Python :: 2 :: Only'],"
 library/pprint,,::,"'Programming Language :: Python :: 2.6',"
 library/pprint,,::,"'Programming Language :: Python :: 2.7',"
+library/pprint,225,::,"'classifiers': ['Development Status :: 3 - Alpha',"
+library/pprint,225,::,"'Intended Audience :: Developers',"
+library/pprint,225,::,"'License :: OSI Approved :: MIT License',"
+library/pprint,225,::,"'Programming Language :: Python :: 2',"
+library/pprint,225,::,"'Programming Language :: Python :: 3',"
+library/pprint,225,::,"'Programming Language :: Python :: 3.2',"
+library/pprint,225,::,"'Programming Language :: Python :: 3.3',"
+library/pprint,225,::,"'Programming Language :: Python :: 3.4',"
+library/pprint,225,::,"'Topic :: Software Development :: Build Tools'],"
 library/profile,,:lineno,filename:lineno(function)
 library/pyexpat,,:elem1,<py:elem1 />
 library/pyexpat,,:py,"xmlns:py = ""http://www.python.org/ns/"">"
index 9c58036..e69e9a3 100644 (file)
@@ -1,12 +1,13 @@
 <h3>{% trans %}Download{% endtrans %}</h3>
 <p><a href="{{ pathto('download') }}">{% trans %}Download these documents{% endtrans %}</a></p>
-<h3>{% trans %}Docs for other versions{% endtrans %}</h3>
+<h3>{% trans %}Docs by version{% endtrans %}</h3>
 <ul>
   <li><a href="https://docs.python.org/3.8/">{% trans %}Python 3.8 (in development){% endtrans %}</a></li>
+  <li><a href="https://docs.python.org/3.7/">{% trans %}Python 3.7 (stable){% endtrans %}</a></li>
   <li><a href="https://docs.python.org/3.6/">{% trans %}Python 3.6 (stable){% endtrans %}</a></li>
   <li><a href="https://docs.python.org/3.5/">{% trans %}Python 3.5 (security-fixes){% endtrans %}</a></li>
   <li><a href="https://docs.python.org/2.7/">{% trans %}Python 2.7 (stable){% endtrans %}</a></li>
-  <li><a href="https://www.python.org/doc/versions/">{% trans %}Old versions{% endtrans %}</a></li>
+  <li><a href="https://www.python.org/doc/versions/">{% trans %}All versions{% endtrans %}</a></li>
 </ul>
 
 <h3>{% trans %}Other resources{% endtrans %}</h3>
index f26838c..914da42 100644 (file)
@@ -672,6 +672,9 @@ be treated as a non-public part of the API (whether it is a function, a method
 or a data member).  It should be considered an implementation detail and subject
 to change without notice.
 
+.. index::
+   pair: name; mangling
+
 Since there is a valid use-case for class-private members (namely to avoid name
 clashes of names with names defined by subclasses), there is limited support for
 such a mechanism, called :dfn:`name mangling`.  Any identifier of the form
@@ -703,6 +706,11 @@ breaking intraclass method calls.  For example::
            for item in zip(keys, values):
                self.items_list.append(item)
 
+The above example would work even if ``MappingSubclass`` were to introduce a
+``__update`` identifier since it is replaced with ``_Mapping__update`` in the
+``Mapping`` class  and ``_MappingSubclass__update`` in the ``MappingSubclass``
+class respectively.
+
 Note that the mangling rules are designed mostly to avoid accidents; it still is
 possible to access or modify a variable that is considered private.  This can
 even be useful in special circumstances, such as in the debugger.
index 4bcdafd..bf6fbe2 100644 (file)
@@ -526,7 +526,7 @@ Arbitrary Argument Lists
 ------------------------
 
 .. index::
-  statement: *
+   single: * (asterisk); in function calls
 
 Finally, the least frequently used option is to specify that a function can be
 called with an arbitrary number of arguments.  These arguments will be wrapped
@@ -570,10 +570,10 @@ or tuple::
    [3, 4, 5]
 
 .. index::
-  statement: **
+   single: **; in function calls
 
-In the same fashion, dictionaries can deliver keyword arguments with the ``**``\
--operator::
+In the same fashion, dictionaries can deliver keyword arguments with the
+``**``\ -operator::
 
    >>> def parrot(voltage, state='a stiff', action='voom'):
    ...     print("-- This parrot wouldn't", action, end=' ')
@@ -675,7 +675,8 @@ Function Annotations
 .. sectionauthor:: Zachary Ware <zachary.ware@gmail.com>
 .. index::
    pair: function; annotations
-   single: -> (return annotation assignment)
+   single: ->; function annotations
+   single: : (colon); function annotations
 
 :ref:`Function annotations <function>` are completely optional metadata
 information about the types used by user-defined functions (see :pep:`3107` and
index 95dc0f9..b291d11 100644 (file)
@@ -41,7 +41,7 @@ objects:
    :noindex:
 
    Remove the first item from the list whose value is equal to *x*.  It raises a
-   ``ValueError`` if there is no such item.
+   :exc:`ValueError` if there is no such item.
 
 
 .. method:: list.pop([i])
index aba61da..957cbf9 100644 (file)
@@ -314,7 +314,7 @@ to create specific exception classes for different error conditions::
            self.next = next
            self.message = message
 
-Most exceptions are defined with names that end in "Error," similar to the
+Most exceptions are defined with names that end in "Error", similar to the
 naming of the standard exceptions.
 
 Many standard modules define their own exceptions to report errors that may
index e68c9b1..3e0c995 100644 (file)
@@ -11,6 +11,8 @@ with a prompt are output from the interpreter. Note that a secondary prompt on a
 line by itself in an example means you must type a blank line; this is used to
 end a multi-line command.
 
+.. index:: single: # (hash); comment
+
 Many of the examples in this manual, even those entered at the interactive
 prompt, include comments.  Comments in Python start with the hash character,
 ``#``, and extend to the end of the physical line.  A comment may appear at the
index f25e02b..dca89ec 100644 (file)
@@ -769,7 +769,7 @@ conflict.
    This may also be enabled at runtime with
    :func:`sys._enablelegacywindowsfsencoding()`.
 
-   Availability: Windows
+   .. availability:: Windows.
 
    .. versionadded:: 3.6
       See :pep:`529` for more details.
@@ -783,7 +783,7 @@ conflict.
    This variable is ignored if the standard streams are redirected (to files
    or pipes) rather than referring to console buffers.
 
-   Availability: Windows
+   .. availability:: Windows.
 
    .. versionadded:: 3.6
 
@@ -834,7 +834,7 @@ conflict.
    order to force the interpreter to use ``ASCII`` instead of ``UTF-8`` for
    system interfaces.
 
-   Availability: \*nix
+   .. availability:: \*nix.
 
    .. versionadded:: 3.7
       See :pep:`538` for more details.
@@ -895,7 +895,7 @@ conflict.
 
    Also available as the :option:`-X` ``utf8`` option.
 
-   Availability: \*nix
+   .. availability:: \*nix.
 
    .. versionadded:: 3.7
       See :pep:`540` for more details.
index 8b392f8..d18fd06 100644 (file)
@@ -140,7 +140,7 @@ Editors and IDEs
 ================
 
 There are a number of IDEs that support Python programming language.
-Many editors and IDEs provide syntax highlighting, debugging tools, and PEP-8 checks.
+Many editors and IDEs provide syntax highlighting, debugging tools, and :pep:`8` checks.
 
 Please go to `Python Editors <https://wiki.python.org/moin/PythonEditors>`_ and
 `Integrated Development Environments <https://wiki.python.org/moin/IntegratedDevelopmentEnvironments>`_
index 272090d..45abd59 100644 (file)
@@ -82,7 +82,8 @@ path.
 
 Once a virtual environment has been created, it can be "activated" using a
 script in the virtual environment's binary directory. The invocation of the
-script is platform-specific:
+script is platform-specific (`<venv>` must be replaced by the path of the
+directory containing the virtual environment):
 
 +-------------+-----------------+-----------------------------------------+
 | Platform    | Shell           | Command to activate virtual environment |
index 3ed39e6..8654bc2 100644 (file)
@@ -12,9 +12,6 @@
 This document aims to give an overview of Windows-specific behaviour you should
 know about when using Python on Microsoft Windows.
 
-Installing Python
-=================
-
 Unlike most Unix systems and services, Windows does not include a system
 supported installation of Python. To make Python available, the CPython team
 has compiled Windows installers (MSI packages) with every `release
@@ -24,15 +21,37 @@ core interpreter and library being used by a single user. The installer is also
 able to install for all users of a single machine, and a separate ZIP file is
 available for application-local distributions.
 
-Supported Versions
-------------------
-
 As specified in :pep:`11`, a Python release only supports a Windows platform
 while Microsoft considers the platform under extended support. This means that
 Python |version| supports Windows Vista and newer. If you require Windows XP
 support then please install Python 3.4.
 
-Installation Steps
+There are a number of different installers available for Windows, each with
+certain benefits and downsides.
+
+:ref:`windows-full` contains all components and is the best option for
+developers using Python for any kind of project.
+
+:ref:`windows-store` is a simple installation of Python that is suitable for
+running scripts and packages, and using IDLE or other development environments.
+It requires Windows 10, but can be safely installed without corrupting other
+programs. It also provides many convenient commands for launching Python and
+its tools.
+
+:ref:`windows-nuget` are lightweight installations intended for continuous
+integration systems. It can be used to build Python packages or run scripts,
+but is not updateable and has no user interface tools.
+
+:ref:`windows-embeddable` is a minimal package of Python suitable for
+embedding into a larger application.
+
+
+.. _windows-full:
+
+The full installer
+==================
+
+Installation steps
 ------------------
 
 Four Python |version| installers are available for download - two each for the
@@ -264,39 +283,199 @@ settings and replace any that have been removed or modified.
 "Uninstall" will remove Python entirely, with the exception of the
 :ref:`launcher`, which has its own entry in Programs and Features.
 
-Other Platforms
----------------
 
-With ongoing development of Python, some platforms that used to be supported
-earlier are no longer supported (due to the lack of users or developers).
-Check :pep:`11` for details on all unsupported platforms.
+.. _windows-store:
 
-* `Windows CE <http://pythonce.sourceforge.net/>`_ is still supported.
-* The `Cygwin <https://cygwin.com/>`_ installer offers to install the Python
-  interpreter as well (cf. `Cygwin package source
-  <ftp://ftp.uni-erlangen.de/pub/pc/gnuwin32/cygwin/mirrors/cygnus/
-  release/python>`_, `Maintainer releases
-  <http://www.tishler.net/jason/software/python/>`_)
+The Microsoft Store package
+===========================
 
-See `Python for Windows <https://www.python.org/downloads/windows/>`_
-for detailed information about platforms with pre-compiled installers.
+.. versionadded:: 3.7.2
 
-.. seealso::
+.. note::
+   The Microsoft Store package is currently considered unstable while its
+   interactions with other tools and other copies of Python are evaluated.
+   While Python itself is stable, this installation method may change its
+   behavior and capabilities during Python 3.7 releases.
 
-   `Python on XP <http://dooling.com/index.php/2006/03/14/python-on-xp-7-minutes-to-hello-world/>`_
-      "7 Minutes to "Hello World!""
-      by Richard Dooling, 2006
+The Microsoft Store package is an easily installable Python interpreter that
+is intended mainly for interactive use, for example, by students.
 
-   `Installing on Windows <http://www.diveintopython.net/installing_python/windows.html>`_
-      in "`Dive into Python: Python from novice to pro
-      <http://www.diveintopython.net/>`_"
-      by Mark Pilgrim, 2004,
-      ISBN 1-59059-356-1
+To install the package, ensure you have the latest Windows 10 updates and
+search the Microsoft Store app for "Python |version|". Ensure that the app
+you select is published by the Python Software Foundation, and install it.
 
-   `For Windows users <https://python.swaroopch.com/installation.html#installation-on-windows>`_
-      in "Installing Python"
-      in "`A Byte of Python <https://python.swaroopch.com/>`_"
-      by Swaroop C H, 2003
+.. warning::
+   Python will always be available for free on the Microsoft Store. If you
+   are asked to pay for it, you have not selected the correct package.
+
+After installation, Python may be launched by finding it in Start.
+Alternatively, it will be available from any Command Prompt or PowerShell
+session by typing ``python``. Further, pip and IDLE may be used by typing
+``pip`` or ``idle``. IDLE can also be found in Start.
+
+All three commands are also available with version number suffixes, for
+example, as ``python3.exe`` and ``python3.x.exe`` as well as
+``python.exe`` (where ``3.x`` is the specific version you want to launch,
+such as |version|).
+
+Virtual environments can be created with ``python -m venv`` and activated
+and used as normal.
+
+If you have installed another version of Python and added it to your
+``PATH`` variable, it will be available as ``python.exe`` rather than the
+one from the Microsoft Store. To access the new installation, use
+``python3.exe`` or ``python3.x.exe``.
+
+To remove Python, open Settings and use Apps and Features, or else find
+Python in Start and right-click to select Uninstall. Uninstalling will
+remove all packages you installed directly into this Python installation, but
+will not remove any virtual environments
+
+Known Issues
+------------
+
+Currently, the ``py.exe`` launcher cannot be used to start Python when it
+has been installed from the Microsoft Store.
+
+Because of restrictions on Microsoft Store apps, Python scripts may not have
+full write access to shared locations such as ``TEMP`` and the registry.
+Instead, it will write to a private copy. If your scripts must modify the
+shared locations, you will need to install the full installer.
+
+
+.. _windows-nuget:
+
+The nuget.org packages
+======================
+
+.. versionadded:: 3.5.2
+
+The nuget.org package is a reduced size Python environment intended for use on
+continuous integration and build systems that do not have a system-wide
+install of Python. While nuget is "the package manager for .NET", it also works
+perfectly fine for packages containing build-time tools.
+
+Visit `nuget.org <https://www.nuget.org/>`_ for the most up-to-date information
+on using nuget. What follows is a summary that is sufficient for Python
+developers.
+
+The ``nuget.exe`` command line tool may be downloaded directly from
+``https://aka.ms/nugetclidl``, for example, using curl or PowerShell. With the
+tool, the latest version of Python for 64-bit or 32-bit machines is installed
+using::
+
+   nuget.exe install python -ExcludeVersion -OutputDirectory .
+   nuget.exe install pythonx86 -ExcludeVersion -OutputDirectory .
+
+To select a particular version, add a ``-Version 3.x.y``. The output directory
+may be changed from ``.``, and the package will be installed into a
+subdirectory. By default, the subdirectory is named the same as the package,
+and without the ``-ExcludeVersion`` option this name will include the specific
+version installed. Inside the subdirectory is a ``tools`` directory that
+contains the Python installation::
+
+   # Without -ExcludeVersion
+   > .\python.3.5.2\tools\python.exe -V
+   Python 3.5.2
+
+   # With -ExcludeVersion
+   > .\python\tools\python.exe -V
+   Python 3.5.2
+
+In general, nuget packages are not upgradeable, and newer versions should be
+installed side-by-side and referenced using the full path. Alternatively,
+delete the package directory manually and install it again. Many CI systems
+will do this automatically if they do not preserve files between builds.
+
+Alongside the ``tools`` directory is a ``build\native`` directory. This
+contains a MSBuild properties file ``python.props`` that can be used in a
+C++ project to reference the Python install. Including the settings will
+automatically use the headers and import libraries in your build.
+
+The package information pages on nuget.org are
+`www.nuget.org/packages/python <https://www.nuget.org/packages/python>`_
+for the 64-bit version and `www.nuget.org/packages/pythonx86
+<https://www.nuget.org/packages/pythonx86>`_ for the 32-bit version.
+
+
+.. _windows-embeddable:
+
+The embeddable package
+======================
+
+.. versionadded:: 3.5
+
+The embedded distribution is a ZIP file containing a minimal Python environment.
+It is intended for acting as part of another application, rather than being
+directly accessed by end-users.
+
+When extracted, the embedded distribution is (almost) fully isolated from the
+user's system, including environment variables, system registry settings, and
+installed packages. The standard library is included as pre-compiled and
+optimized ``.pyc`` files in a ZIP, and ``python3.dll``, ``python37.dll``,
+``python.exe`` and ``pythonw.exe`` are all provided. Tcl/tk (including all
+dependants, such as Idle), pip and the Python documentation are not included.
+
+.. note::
+
+    The embedded distribution does not include the `Microsoft C Runtime
+    <https://www.microsoft.com/en-us/download/details.aspx?id=48145>`_ and it is
+    the responsibility of the application installer to provide this. The
+    runtime may have already been installed on a user's system previously or
+    automatically via Windows Update, and can be detected by finding
+    ``ucrtbase.dll`` in the system directory.
+
+Third-party packages should be installed by the application installer alongside
+the embedded distribution. Using pip to manage dependencies as for a regular
+Python installation is not supported with this distribution, though with some
+care it may be possible to include and use pip for automatic updates. In
+general, third-party packages should be treated as part of the application
+("vendoring") so that the developer can ensure compatibility with newer
+versions before providing updates to users.
+
+The two recommended use cases for this distribution are described below.
+
+Python Application
+------------------
+
+An application written in Python does not necessarily require users to be aware
+of that fact. The embedded distribution may be used in this case to include a
+private version of Python in an install package. Depending on how transparent it
+should be (or conversely, how professional it should appear), there are two
+options.
+
+Using a specialized executable as a launcher requires some coding, but provides
+the most transparent experience for users. With a customized launcher, there are
+no obvious indications that the program is running on Python: icons can be
+customized, company and version information can be specified, and file
+associations behave properly. In most cases, a custom launcher should simply be
+able to call ``Py_Main`` with a hard-coded command line.
+
+The simpler approach is to provide a batch file or generated shortcut that
+directly calls the ``python.exe`` or ``pythonw.exe`` with the required
+command-line arguments. In this case, the application will appear to be Python
+and not its actual name, and users may have trouble distinguishing it from other
+running Python processes or file associations.
+
+With the latter approach, packages should be installed as directories alongside
+the Python executable to ensure they are available on the path. With the
+specialized launcher, packages can be located in other locations as there is an
+opportunity to specify the search path before launching the application.
+
+Embedding Python
+----------------
+
+Applications written in native code often require some form of scripting
+language, and the embedded Python distribution can be used for this purpose. In
+general, the majority of the application is in native code, and some part will
+either invoke ``python.exe`` or directly use ``python3.dll``. For either case,
+extracting the embedded distribution to a subdirectory of the application
+installation is sufficient to provide a loadable Python interpreter.
+
+As with the application use, packages can be installed to any location as there
+is an opportunity to specify search paths before initializing the interpreter.
+Otherwise, there is no fundamental differences between using the embedded
+distribution and a regular installation.
 
 
 Alternative bundles
@@ -441,6 +620,8 @@ appropriate version of Python. It will prefer per-user installations over
 system-wide ones, and orders by language version rather than using the most
 recently installed version.
 
+The launcher was originally specified in :pep:`397`.
+
 Getting started
 ---------------
 
@@ -613,11 +794,11 @@ user's "application data" directory (i.e. the directory returned by calling the
 Windows function ``SHGetFolderPath`` with ``CSIDL_LOCAL_APPDATA``) and ``py.ini`` in the
 same directory as the launcher. The same .ini files are used for both the
 'console' version of the launcher (i.e. py.exe) and for the 'windows' version
-(i.e. pyw.exe)
+(i.e. pyw.exe).
 
 Customization specified in the "application directory" will have precedence over
 the one next to the executable, so a user, who may not have write access to the
-.ini file next to the launcher, can override commands in that global .ini file)
+.ini file next to the launcher, can override commands in that global .ini file.
 
 Customizing default Python versions
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -922,95 +1103,19 @@ For extension modules, consult :ref:`building-on-windows`.
       by Trent Apted et al, 2007
 
 
-Embedded Distribution
-=====================
-
-.. versionadded:: 3.5
-
-The embedded distribution is a ZIP file containing a minimal Python environment.
-It is intended for acting as part of another application, rather than being
-directly accessed by end-users.
-
-When extracted, the embedded distribution is (almost) fully isolated from the
-user's system, including environment variables, system registry settings, and
-installed packages. The standard library is included as pre-compiled and
-optimized ``.pyc`` files in a ZIP, and ``python3.dll``, ``python37.dll``,
-``python.exe`` and ``pythonw.exe`` are all provided. Tcl/tk (including all
-dependants, such as Idle), pip and the Python documentation are not included.
-
-.. note::
-
-    The embedded distribution does not include the `Microsoft C Runtime
-    <https://www.microsoft.com/en-us/download/details.aspx?id=48145>`_ and it is
-    the responsibility of the application installer to provide this. The
-    runtime may have already been installed on a user's system previously or
-    automatically via Windows Update, and can be detected by finding
-    ``ucrtbase.dll`` in the system directory.
-
-Third-party packages should be installed by the application installer alongside
-the embedded distribution. Using pip to manage dependencies as for a regular
-Python installation is not supported with this distribution, though with some
-care it may be possible to include and use pip for automatic updates. In
-general, third-party packages should be treated as part of the application
-("vendoring") so that the developer can ensure compatibility with newer
-versions before providing updates to users.
-
-The two recommended use cases for this distribution are described below.
-
-Python Application
-------------------
-
-An application written in Python does not necessarily require users to be aware
-of that fact. The embedded distribution may be used in this case to include a
-private version of Python in an install package. Depending on how transparent it
-should be (or conversely, how professional it should appear), there are two
-options.
-
-Using a specialized executable as a launcher requires some coding, but provides
-the most transparent experience for users. With a customized launcher, there are
-no obvious indications that the program is running on Python: icons can be
-customized, company and version information can be specified, and file
-associations behave properly. In most cases, a custom launcher should simply be
-able to call ``Py_Main`` with a hard-coded command line.
-
-The simpler approach is to provide a batch file or generated shortcut that
-directly calls the ``python.exe`` or ``pythonw.exe`` with the required
-command-line arguments. In this case, the application will appear to be Python
-and not its actual name, and users may have trouble distinguishing it from other
-running Python processes or file associations.
-
-With the latter approach, packages should be installed as directories alongside
-the Python executable to ensure they are available on the path. With the
-specialized launcher, packages can be located in other locations as there is an
-opportunity to specify the search path before launching the application.
-
-Embedding Python
-----------------
-
-Applications written in native code often require some form of scripting
-language, and the embedded Python distribution can be used for this purpose. In
-general, the majority of the application is in native code, and some part will
-either invoke ``python.exe`` or directly use ``python3.dll``. For either case,
-extracting the embedded distribution to a subdirectory of the application
-installation is sufficient to provide a loadable Python interpreter.
-
-As with the application use, packages can be installed to any location as there
-is an opportunity to specify search paths before initializing the interpreter.
-Otherwise, there is no fundamental differences between using the embedded
-distribution and a regular installation.
-
-Other resources
+Other Platforms
 ===============
 
-.. seealso::
-
-   `Python Programming On Win32 <http://shop.oreilly.com/product/9781565926219.do>`_
-      "Help for Windows Programmers"
-      by Mark Hammond and Andy Robinson, O'Reilly Media, 2000,
-      ISBN 1-56592-621-8
+With ongoing development of Python, some platforms that used to be supported
+earlier are no longer supported (due to the lack of users or developers).
+Check :pep:`11` for details on all unsupported platforms.
 
-   `A Python for Windows Tutorial <http://www.imladris.com/Scripts/PythonForWindows.html>`_
-      by Amanda Birmingham, 2004
+* `Windows CE <http://pythonce.sourceforge.net/>`_ is still supported.
+* The `Cygwin <https://cygwin.com/>`_ installer offers to install the Python
+  interpreter as well (cf. `Cygwin package source
+  <ftp://ftp.uni-erlangen.de/pub/pc/gnuwin32/cygwin/mirrors/cygnus/
+  release/python>`_, `Maintainer releases
+  <http://www.tishler.net/jason/software/python/>`_)
 
-   :pep:`397` - Python launcher for Windows
-      The proposal for the launcher to be included in the Python distribution.
+See `Python for Windows <https://www.python.org/downloads/windows/>`_
+for detailed information about platforms with pre-compiled installers.
index 0aa6003..c2ae866 100644 (file)
@@ -576,9 +576,9 @@ similar to a :keyword:`return` statement.  The big difference between
 variables are preserved.  On the next call to the generator's ``next()`` method,
 the function will resume executing immediately after the :keyword:`yield`
 statement.  (For complicated reasons, the :keyword:`yield` statement isn't
-allowed inside the :keyword:`try` block of a :keyword:`try`...\
-:keyword:`finally` statement; read :pep:`255` for a full explanation of the
-interaction between :keyword:`yield` and exceptions.)
+allowed inside the :keyword:`try` block of a
+:keyword:`try`...\ :keyword:`finally` statement; read :pep:`255` for a full
+explanation of the interaction between :keyword:`yield` and exceptions.)
 
 Here's a sample usage of the :func:`generate_ints` generator::
 
index 590015a..37ba7c0 100644 (file)
@@ -162,9 +162,9 @@ similar to a :keyword:`return` statement.  The big difference between
 variables are preserved.  On the next call to the generator's ``.next()``
 method, the function will resume executing immediately after the
 :keyword:`yield` statement.  (For complicated reasons, the :keyword:`yield`
-statement isn't allowed inside the :keyword:`try` block of a :keyword:`try`...\
-:keyword:`finally` statement; read :pep:`255` for a full explanation of the
-interaction between :keyword:`yield` and exceptions.)
+statement isn't allowed inside the :keyword:`try` block of a
+:keyword:`try`...\ :keyword:`finally` statement; read :pep:`255` for a full
+explanation of the interaction between :keyword:`yield` and exceptions.)
 
 Here's a sample usage of the :func:`generate_ints` generator::
 
@@ -1247,8 +1247,8 @@ complete list of changes, or look through the CVS logs for all the details.
   will have to change your ``import`` statements to import it as :mod:`bsddb`.
 
 * The new :mod:`bz2` module is an interface to the bz2 data compression library.
-  bz2-compressed data is usually smaller than  corresponding :mod:`zlib`\
-  -compressed data. (Contributed by Gustavo Niemeyer.)
+  bz2-compressed data is usually smaller than  corresponding
+  :mod:`zlib`\ -compressed data. (Contributed by Gustavo Niemeyer.)
 
 * A set of standard date/time types has been added in the new :mod:`datetime`
   module.  See the following section for more details.
index 79c5a73..7a3384c 100644 (file)
@@ -448,8 +448,9 @@ when you're doing something with the returned value, as in the above example.
 The parentheses aren't always necessary, but it's easier to always add them
 instead of having to remember when they're needed.
 
-(:pep:`342` explains the exact rules, which are that a :keyword:`yield`\
--expression must always be parenthesized except when it occurs at the top-level
+(:pep:`342` explains the exact rules, which are that a
+:keyword:`yield`\ -expression must always be parenthesized except when it
+occurs at the top-level
 expression on the right-hand side of an assignment.  This means you can write
 ``val = yield i`` but have to use parentheses when there's an operation, as in
 ``val = (yield i) + 12``.)
index 6597170..1099623 100644 (file)
@@ -1452,10 +1452,10 @@ in :issue:`18143`.)
 
 :class:`~ssl.SSLContext` has a new method,
 :meth:`~ssl.SSLContext.cert_store_stats`, that reports the number of loaded
-``X.509`` certs, ``X.509 CA`` certs, and certificate revocation lists (``crl``\
-s), as well as a :meth:`~ssl.SSLContext.get_ca_certs` method that returns a
-list of the loaded ``CA`` certificates.  (Contributed by Christian Heimes in
-:issue:`18147`.)
+``X.509`` certs, ``X.509 CA`` certs, and certificate revocation lists
+(``crl``\ s), as well as a :meth:`~ssl.SSLContext.get_ca_certs` method that
+returns a list of the loaded ``CA`` certificates.  (Contributed by Christian
+Heimes in :issue:`18147`.)
 
 If OpenSSL 0.9.8 or later is available, :class:`~ssl.SSLContext` has a new
 attribute :attr:`~ssl.SSLContext.verify_flags` that can be used to control the
index 4eddf84..b4540ac 100644 (file)
@@ -593,7 +593,7 @@ a :term:`__future__` import is necessary::
     RuntimeError: generator raised StopIteration
 
 Without a ``__future__`` import, a :exc:`PendingDeprecationWarning` will be
-raised whenever a ``StopIteration`` exception is raised inside a generator.
+raised whenever a :exc:`StopIteration` exception is raised inside a generator.
 
 .. seealso::
 
index fec7c62..936ea2d 100644 (file)
@@ -741,7 +741,7 @@ Some smaller changes made to the core Python language are:
 
 * A ``global`` or ``nonlocal`` statement must now textually appear
   before the first use of the affected name in the same scope.
-  Previously this was a ``SyntaxWarning``.
+  Previously this was a :exc:`SyntaxWarning`.
 
 * It is now possible to set a :ref:`special method <specialnames>` to
   ``None`` to indicate that the corresponding operation is not available.
@@ -2414,3 +2414,11 @@ Notable changes in Python 3.6.5
 The :func:`locale.localeconv` function now sets temporarily the ``LC_CTYPE``
 locale to the ``LC_NUMERIC`` locale in some cases.
 (Contributed by Victor Stinner in :issue:`31900`.)
+
+Notable changes in Python 3.6.7
+===============================
+
+In 3.6.7 the :mod:`tokenize` module now implicitly emits a ``NEWLINE`` token
+when provided with input that does not have a trailing new line.  This behavior
+now matches what the C tokenizer does internally.
+(Contributed by Ammar Askar in :issue:`33899`.)
index 9cc7932..7d4c4f9 100644 (file)
@@ -432,7 +432,7 @@ and size) with source metadata saved in the cache file header when it was
 generated. While effective, this invalidation method has its drawbacks.  When
 filesystem timestamps are too coarse, Python can miss source updates, leading to
 user confusion. Additionally, having a timestamp in the cache file is
-problematic for `build reproduciblity <https://reproducible-builds.org/>`_ and
+problematic for `build reproducibility <https://reproducible-builds.org/>`_ and
 content-based build systems.
 
 :pep:`552` extends the pyc format to allow the hash of the source file to be
@@ -1030,7 +1030,7 @@ support the loading of resources from packages.  See also
 lacks a spec.
 (Contributed by Garvit Khatri in :issue:`29851`.)
 
-:func:`importlib.find_spec` now raises ``ModuleNotFoundError`` instead of
+:func:`importlib.find_spec` now raises :exc:`ModuleNotFoundError` instead of
 :exc:`AttributeError` if the specified parent module is not a package (i.e.
 lacks a ``__path__`` attribute).
 (Contributed by Milan Oberkirch in :issue:`30436`.)
@@ -2523,3 +2523,18 @@ In 3.7.1 the C API for Context Variables
 
 :mod:`xml.dom.minidom` and :mod:`xml.sax` modules no longer process
 external entities by default. See also :issue:`17239`.
+
+In 3.7.1 the :mod:`tokenize` module now implicitly emits a ``NEWLINE`` token
+when provided with input that does not have a trailing new line.  This behavior
+now matches what the C tokenizer does internally.
+(Contributed by Ammar Askar in :issue:`33899`.)
+
+Notable changes in Python 3.7.2
+===============================
+
+In 3.7.2, :mod:`venv` on Windows no longer copies the original binaries, but
+creates redirector scripts named ``python.exe`` and ``pythonw.exe`` instead.
+This resolves a long standing issue where all virtual environments would have
+to be upgraded or recreated with each Python update. However, note that this
+release will still require recreation of virtual environments in order to get
+the new scripts.
index 1feb153..0511373 100644 (file)
 #include "pyport.h"
 #include "pymacro.h"
 
+/* A convenient way for code to know if clang's memory sanitizer is enabled. */
+#if defined(__has_feature)
+#  if __has_feature(memory_sanitizer)
+#    if !defined(_Py_MEMORY_SANITIZER)
+#      define _Py_MEMORY_SANITIZER
+#    endif
+#  endif
+#endif
+
 #include "pyatomic.h"
 
 /* Debug-mode build with pymalloc implies PYMALLOC_DEBUG.
index bce8a0b..11283c0 100644 (file)
@@ -48,10 +48,12 @@ PyAPI_FUNC(PyObject *) PyEval_GetGlobals(void);
 PyAPI_FUNC(PyObject *) PyEval_GetLocals(void);
 PyAPI_FUNC(struct _frame *) PyEval_GetFrame(void);
 
+#ifndef Py_LIMITED_API
+/* Helper to look up a builtin object */
+PyAPI_FUNC(PyObject *) _PyEval_GetBuiltinId(_Py_Identifier *);
 /* Look at the current frame's (if any) code's co_flags, and turn on
    the corresponding compiler flags in cf->cf_flags.  Return 1 if any
    flag was set, else return 0. */
-#ifndef Py_LIMITED_API
 PyAPI_FUNC(int) PyEval_MergeCompilerFlags(PyCompilerFlags *cf);
 #endif
 
index e4bf6d4..419d49a 100644 (file)
@@ -60,6 +60,19 @@ PyAPI_FUNC(int) _Py_EncodeLocaleEx(
 #ifndef Py_LIMITED_API
 PyAPI_FUNC(PyObject *) _Py_device_encoding(int);
 
+#if defined(MS_WINDOWS) || defined(__APPLE__)
+    /* On Windows, the count parameter of read() is an int (bpo-9015, bpo-9611).
+       On macOS 10.13, read() and write() with more than INT_MAX bytes
+       fail with EINVAL (bpo-24658). */
+#   define _PY_READ_MAX  INT_MAX
+#   define _PY_WRITE_MAX INT_MAX
+#else
+    /* write() should truncate the input to PY_SSIZE_T_MAX bytes,
+       but it's safer to do it ourself to have a portable behaviour */
+#   define _PY_READ_MAX  PY_SSIZE_T_MAX
+#   define _PY_WRITE_MAX PY_SSIZE_T_MAX
+#endif
+
 #ifdef MS_WINDOWS
 struct _Py_stat_struct {
     unsigned long st_dev;
@@ -170,6 +183,17 @@ PyAPI_FUNC(int) _Py_GetLocaleconvNumeric(
 
 #endif   /* Py_LIMITED_API */
 
+#ifdef Py_BUILD_CORE
+PyAPI_FUNC(int) _Py_GetForceASCII(void);
+
+/* Reset "force ASCII" mode (if it was initialized).
+
+   This function should be called when Python changes the LC_CTYPE locale,
+   so the "force ASCII" mode can be detected again on the new locale
+   encoding. */
+PyAPI_FUNC(void) _Py_ResetForceASCII(void);
+#endif
+
 #ifdef __cplusplus
 }
 #endif
index c772dea..bcf78af 100644 (file)
@@ -521,6 +521,7 @@ struct _Py_Identifier;
 PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int);
 PyAPI_FUNC(void) _Py_BreakPoint(void);
 PyAPI_FUNC(void) _PyObject_Dump(PyObject *);
+PyAPI_FUNC(int) _PyObject_IsFreed(PyObject *);
 #endif
 PyAPI_FUNC(PyObject *) PyObject_Repr(PyObject *);
 PyAPI_FUNC(PyObject *) PyObject_Str(PyObject *);
index 667facb..cd35daa 100644 (file)
 /*--start constants--*/
 #define PY_MAJOR_VERSION        3
 #define PY_MINOR_VERSION        7
-#define PY_MICRO_VERSION        1
+#define PY_MICRO_VERSION        2
 #define PY_RELEASE_LEVEL        PY_RELEASE_LEVEL_FINAL
 #define PY_RELEASE_SERIAL       0
 
 /* Version as a string */
-#define PY_VERSION              "3.7.1"
+#define PY_VERSION              "3.7.2"
 /*--end constants--*/
 
 /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2.
index f289471..f49d4e8 100644 (file)
@@ -94,9 +94,9 @@ PyAPI_FUNC(void) PyErr_SetExcInfo(PyObject *, PyObject *, PyObject *);
 #endif
 
 #if defined(__clang__) || \
-    (defined(__GNUC_MAJOR__) && \
-     ((__GNUC_MAJOR__ >= 3) || \
-      (__GNUC_MAJOR__ == 2) && (__GNUC_MINOR__ >= 5)))
+    (defined(__GNUC__) && \
+     ((__GNUC__ >= 3) || \
+      (__GNUC__ == 2) && (__GNUC_MINOR__ >= 5)))
 #define _Py_NO_RETURN __attribute__((__noreturn__))
 #else
 #define _Py_NO_RETURN
index 1192961..05f17dc 100644 (file)
@@ -44,6 +44,8 @@ PyAPI_FUNC(void) Py_SetPythonHome(const wchar_t *);
 PyAPI_FUNC(wchar_t *) Py_GetPythonHome(void);
 
 #ifndef Py_LIMITED_API
+PyAPI_FUNC(void) _Py_SetProgramFullPath(const wchar_t *);
+
 /* Only used by applications that embed the interpreter and need to
  * override the standard encoding determination mechanism
  */
@@ -61,11 +63,14 @@ PyAPI_FUNC(_PyInitError) _Py_InitializeFromConfig(
 PyAPI_FUNC(void) _Py_Initialize_ReadEnvVarsNoAlloc(void);
 #endif
 
+PyAPI_FUNC(PyObject *) _Py_GetGlobalVariablesAsDict(void);
+
 PyAPI_FUNC(_PyInitError) _PyCoreConfig_Read(_PyCoreConfig *);
 PyAPI_FUNC(void) _PyCoreConfig_Clear(_PyCoreConfig *);
 PyAPI_FUNC(int) _PyCoreConfig_Copy(
     _PyCoreConfig *config,
     const _PyCoreConfig *config2);
+PyAPI_FUNC(PyObject *) _PyCoreConfig_AsDict(const _PyCoreConfig *config);
 PyAPI_FUNC(void) _PyCoreConfig_SetGlobalConfig(
     const _PyCoreConfig *config);
 
@@ -77,6 +82,9 @@ PyAPI_FUNC(void) _PyMainInterpreterConfig_Clear(_PyMainInterpreterConfig *);
 PyAPI_FUNC(int) _PyMainInterpreterConfig_Copy(
     _PyMainInterpreterConfig *config,
     const _PyMainInterpreterConfig *config2);
+/* Used by _testcapi.get_main_config() */
+PyAPI_FUNC(PyObject*) _PyMainInterpreterConfig_AsDict(
+    const _PyMainInterpreterConfig *config);
 
 PyAPI_FUNC(_PyInitError) _Py_InitializeMainInterpreter(
         PyInterpreterState *interp,
@@ -91,7 +99,9 @@ PyAPI_FUNC(void) Py_InitializeEx(int);
 PyAPI_FUNC(void) _Py_FatalInitError(_PyInitError err) _Py_NO_RETURN;
 #endif
 PyAPI_FUNC(void) Py_Finalize(void);
+#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03060000
 PyAPI_FUNC(int) Py_FinalizeEx(void);
+#endif
 PyAPI_FUNC(int) Py_IsInitialized(void);
 
 /* Subinterpreter support */
index 8ee0efd..ef6e0bb 100644 (file)
@@ -55,7 +55,9 @@ PyAPI_FUNC(int) PyTraceMalloc_Untrack(
 PyAPI_FUNC(PyObject*) _PyTraceMalloc_GetTraceback(
     unsigned int domain,
     uintptr_t ptr);
-#endif   /* !Py_LIMITED_API */
+
+PyAPI_FUNC(int) _PyMem_IsFreed(void *ptr, size_t size);
+#endif   /* !defined(Py_LIMITED_API) */
 
 
 /* BEWARE:
index 0274de6..45998a1 100644 (file)
@@ -2135,10 +2135,10 @@ PyAPI_FUNC(PyObject *) _PyUnicode_XStrip(
    see Objects/stringlib/localeutil.h */
 #ifndef Py_LIMITED_API
 PyAPI_FUNC(Py_ssize_t) _PyUnicode_InsertThousandsGrouping(
-    PyObject *unicode,
-    Py_ssize_t index,
+    _PyUnicodeWriter *writer,
     Py_ssize_t n_buffer,
-    void *digits,
+    PyObject *digits,
+    Py_ssize_t d_pos,
     Py_ssize_t n_digits,
     Py_ssize_t min_width,
     const char *grouping,
index e37852e..db6674e 100644 (file)
@@ -17,7 +17,7 @@ __all__ = [
 _UNIVERSAL_CONFIG_VARS = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS', 'BASECFLAGS',
                             'BLDSHARED', 'LDSHARED', 'CC', 'CXX',
                             'PY_CFLAGS', 'PY_LDFLAGS', 'PY_CPPFLAGS',
-                            'PY_CORE_CFLAGS')
+                            'PY_CORE_CFLAGS', 'PY_CORE_LDFLAGS')
 
 # configuration variables that may contain compiler calls
 _COMPILER_CONFIG_VARS = ('BLDSHARED', 'LDSHARED', 'CC', 'CXX')
index 3596900..44ea5b4 100644 (file)
@@ -2021,7 +2021,7 @@ class Decimal(object):
         if not other and not self:
             return context._raise_error(InvalidOperation,
                                         'at least one of pow() 1st argument '
-                                        'and 2nd argument must be nonzero ;'
+                                        'and 2nd argument must be nonzero'
                                         '0**0 is not defined')
 
         # compute sign of result
index b3b0755..65e0529 100644 (file)
@@ -165,6 +165,17 @@ def _run_until_complete_cb(fut):
     futures._get_loop(fut).stop()
 
 
+if hasattr(socket, 'TCP_NODELAY'):
+    def _set_nodelay(sock):
+        if (sock.family in {socket.AF_INET, socket.AF_INET6} and
+                sock.type == socket.SOCK_STREAM and
+                sock.proto == socket.IPPROTO_TCP):
+            sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
+else:
+    def _set_nodelay(sock):
+        pass
+
+
 class _SendfileFallbackProtocol(protocols.Protocol):
     def __init__(self, transp):
         if not isinstance(transp, transports._FlowControlMixin):
index 66bfb0a..e350e8b 100644 (file)
@@ -444,6 +444,11 @@ class _ProactorSocketTransport(_ProactorReadPipeTransport,
 
     _sendfile_compatible = constants._SendfileMode.TRY_NATIVE
 
+    def __init__(self, loop, sock, protocol, waiter=None,
+                 extra=None, server=None):
+        super().__init__(loop, sock, protocol, waiter, extra, server)
+        base_events._set_nodelay(sock)
+
     def _set_extra(self, sock):
         self._extra['socket'] = sock
 
index 116c08d..d2861d3 100644 (file)
@@ -39,17 +39,6 @@ def _test_selector_event(selector, fd, event):
         return bool(key.events & event)
 
 
-if hasattr(socket, 'TCP_NODELAY'):
-    def _set_nodelay(sock):
-        if (sock.family in {socket.AF_INET, socket.AF_INET6} and
-                sock.type == socket.SOCK_STREAM and
-                sock.proto == socket.IPPROTO_TCP):
-            sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
-else:
-    def _set_nodelay(sock):
-        pass
-
-
 class BaseSelectorEventLoop(base_events.BaseEventLoop):
     """Selector event loop.
 
@@ -733,7 +722,7 @@ class _SelectorSocketTransport(_SelectorTransport):
         # Disable the Nagle algorithm -- small writes will be
         # sent without waiting for the TCP ACK.  This generally
         # decreases the latency (in some cases significantly.)
-        _set_nodelay(self._sock)
+        base_events._set_nodelay(self._sock)
 
         self._loop.call_soon(self._protocol.connection_made, self)
         # only start reading when connection_made() has been called
index c044be8..f6e423b 100755 (executable)
@@ -124,6 +124,7 @@ def main():
     import os
     import sys
     import runpy
+    import pstats
     from optparse import OptionParser
     usage = "cProfile.py [-o output_file_path] [-s sort] [-m module | scriptfile] [arg] ..."
     parser = OptionParser(usage=usage)
@@ -132,7 +133,8 @@ def main():
         help="Save stats to <outfile>", default=None)
     parser.add_option('-s', '--sort', dest="sort",
         help="Sort order when printing to stdout, based on pstats.Stats class",
-        default=-1)
+        default=-1,
+        choices=sorted(pstats.Stats.sort_arg_dict_default))
     parser.add_option('-m', dest="module", action="store_true",
         help="Profile a library module", default=False)
 
index f82cc6c..8cf6687 100755 (executable)
@@ -328,7 +328,8 @@ class FieldStorage:
     """
     def __init__(self, fp=None, headers=None, outerboundary=b'',
                  environ=os.environ, keep_blank_values=0, strict_parsing=0,
-                 limit=None, encoding='utf-8', errors='replace'):
+                 limit=None, encoding='utf-8', errors='replace',
+                 max_num_fields=None):
         """Constructor.  Read multipart/* until last part.
 
         Arguments, all optional:
@@ -368,10 +369,14 @@ class FieldStorage:
             for the page sending the form (content-type : meta http-equiv or
             header)
 
+        max_num_fields: int. If set, then __init__ throws a ValueError
+            if there are more than n fields read by parse_qsl().
+
         """
         method = 'GET'
         self.keep_blank_values = keep_blank_values
         self.strict_parsing = strict_parsing
+        self.max_num_fields = max_num_fields
         if 'REQUEST_METHOD' in environ:
             method = environ['REQUEST_METHOD'].upper()
         self.qs_on_post = None
@@ -595,12 +600,11 @@ class FieldStorage:
         qs = qs.decode(self.encoding, self.errors)
         if self.qs_on_post:
             qs += '&' + self.qs_on_post
-        self.list = []
         query = urllib.parse.parse_qsl(
             qs, self.keep_blank_values, self.strict_parsing,
-            encoding=self.encoding, errors=self.errors)
-        for key, value in query:
-            self.list.append(MiniFieldStorage(key, value))
+            encoding=self.encoding, errors=self.errors,
+            max_num_fields=self.max_num_fields)
+        self.list = [MiniFieldStorage(key, value) for key, value in query]
         self.skip_lines()
 
     FieldStorageClass = None
@@ -614,9 +618,9 @@ class FieldStorage:
         if self.qs_on_post:
             query = urllib.parse.parse_qsl(
                 self.qs_on_post, self.keep_blank_values, self.strict_parsing,
-                encoding=self.encoding, errors=self.errors)
-            for key, value in query:
-                self.list.append(MiniFieldStorage(key, value))
+                encoding=self.encoding, errors=self.errors,
+                max_num_fields=self.max_num_fields)
+            self.list.extend(MiniFieldStorage(key, value) for key, value in query)
 
         klass = self.FieldStorageClass or self.__class__
         first_line = self.fp.readline() # bytes
@@ -631,6 +635,11 @@ class FieldStorage:
             first_line = self.fp.readline()
             self.bytes_read += len(first_line)
 
+        # Propagate max_num_fields into the sub class appropriately
+        max_num_fields = self.max_num_fields
+        if max_num_fields is not None:
+            max_num_fields -= len(self.list)
+
         while True:
             parser = FeedParser()
             hdr_text = b""
@@ -652,7 +661,15 @@ class FieldStorage:
 
             part = klass(self.fp, headers, ib, environ, keep_blank_values,
                          strict_parsing,self.limit-self.bytes_read,
-                         self.encoding, self.errors)
+                         self.encoding, self.errors, max_num_fields)
+
+            if max_num_fields is not None:
+                max_num_fields -= 1
+                if part.list:
+                    max_num_fields -= len(part.list)
+                if max_num_fields < 0:
+                    raise ValueError('Max number of fields exceeded')
+
             self.bytes_read += part.bytes_read
             self.list.append(part)
             if part.done or self.bytes_read >= self.length > 0:
index 7259212..aa65c6b 100644 (file)
@@ -16,10 +16,6 @@ import importlib.util
 import py_compile
 import struct
 
-try:
-    from concurrent.futures import ProcessPoolExecutor
-except ImportError:
-    ProcessPoolExecutor = None
 from functools import partial
 
 __all__ = ["compile_dir","compile_file","compile_path"]
@@ -53,7 +49,7 @@ def _walk_dir(dir, ddir=None, maxlevels=10, quiet=0):
 
 def compile_dir(dir, maxlevels=10, ddir=None, force=False, rx=None,
                 quiet=0, legacy=False, optimize=-1, workers=1,
-                invalidation_mode=py_compile.PycInvalidationMode.TIMESTAMP):
+                invalidation_mode=None):
     """Byte-compile all modules in the given directory tree.
 
     Arguments (only dir is required):
@@ -70,9 +66,17 @@ def compile_dir(dir, maxlevels=10, ddir=None, force=False, rx=None,
     workers:   maximum number of parallel workers
     invalidation_mode: how the up-to-dateness of the pyc will be checked
     """
-    if workers is not None and workers < 0:
-        raise ValueError('workers must be greater or equal to 0')
-
+    ProcessPoolExecutor = None
+    if workers is not None:
+        if workers < 0:
+            raise ValueError('workers must be greater or equal to 0')
+        elif workers != 1:
+            try:
+                # Only import when needed, as low resource platforms may
+                # fail to import it
+                from concurrent.futures import ProcessPoolExecutor
+            except ImportError:
+                workers = 1
     files = _walk_dir(dir, quiet=quiet, maxlevels=maxlevels,
                       ddir=ddir)
     success = True
@@ -96,7 +100,7 @@ def compile_dir(dir, maxlevels=10, ddir=None, force=False, rx=None,
 
 def compile_file(fullname, ddir=None, force=False, rx=None, quiet=0,
                  legacy=False, optimize=-1,
-                 invalidation_mode=py_compile.PycInvalidationMode.TIMESTAMP):
+                 invalidation_mode=None):
     """Byte-compile one file.
 
     Arguments (only fullname is required):
@@ -182,7 +186,7 @@ def compile_file(fullname, ddir=None, force=False, rx=None, quiet=0,
 
 def compile_path(skip_curdir=1, maxlevels=0, force=False, quiet=0,
                  legacy=False, optimize=-1,
-                 invalidation_mode=py_compile.PycInvalidationMode.TIMESTAMP):
+                 invalidation_mode=None):
     """Byte-compile all module on sys.path.
 
     Arguments (all optional):
@@ -255,9 +259,12 @@ def main():
                         type=int, help='Run compileall concurrently')
     invalidation_modes = [mode.name.lower().replace('_', '-')
                           for mode in py_compile.PycInvalidationMode]
-    parser.add_argument('--invalidation-mode', default='timestamp',
+    parser.add_argument('--invalidation-mode',
                         choices=sorted(invalidation_modes),
-                        help='How the pycs will be invalidated at runtime')
+                        help=('set .pyc invalidation mode; defaults to '
+                              '"checked-hash" if the SOURCE_DATE_EPOCH '
+                              'environment variable is set, and '
+                              '"timestamp" otherwise.'))
 
     args = parser.parse_args()
     compile_dests = args.compile_dest
@@ -286,8 +293,11 @@ def main():
     if args.workers is not None:
         args.workers = args.workers or None
 
-    ivl_mode = args.invalidation_mode.replace('-', '_').upper()
-    invalidation_mode = py_compile.PycInvalidationMode[ivl_mode]
+    if args.invalidation_mode:
+        ivl_mode = args.invalidation_mode.replace('-', '_').upper()
+        invalidation_mode = py_compile.PycInvalidationMode[ivl_mode]
+    else:
+        invalidation_mode = None
 
     success = True
     try:
index b65dee1..7835971 100644 (file)
@@ -150,7 +150,7 @@ class ThreadPoolExecutor(_base.Executor):
             if self._shutdown:
                 raise RuntimeError('cannot schedule new futures after shutdown')
             if _shutdown:
-                raise RuntimeError('cannot schedule new futures after'
+                raise RuntimeError('cannot schedule new futures after '
                                    'interpreter shutdown')
 
             f = _base.Future()
index 20fa056..092ec5a 100644 (file)
@@ -12,6 +12,7 @@ class BytesTest(unittest.TestCase):
             x.value = "y"
         c_char.from_param(b"x")
         self.assertRaises(TypeError, c_char.from_param, "x")
+        self.assertIn('xbd', repr(c_char.from_param(b"\xbd")))
         (c_char * 3)(b"a", b"b", b"c")
         self.assertRaises(TypeError, c_char * 3, "a", "b", "c")
 
index 28e9f75..71d9896 100644 (file)
@@ -5,6 +5,9 @@ import types
 import inspect
 import keyword
 import builtins
+import functools
+import _thread
+
 
 __all__ = ['dataclass',
            'field',
@@ -337,6 +340,27 @@ def _tuple_str(obj_name, fields):
     return f'({",".join([f"{obj_name}.{f.name}" for f in fields])},)'
 
 
+# This function's logic is copied from "recursive_repr" function in
+# reprlib module to avoid dependency.
+def _recursive_repr(user_function):
+    # Decorator to make a repr function return "..." for a recursive
+    # call.
+    repr_running = set()
+
+    @functools.wraps(user_function)
+    def wrapper(self):
+        key = id(self), _thread.get_ident()
+        if key in repr_running:
+            return '...'
+        repr_running.add(key)
+        try:
+            result = user_function(self)
+        finally:
+            repr_running.discard(key)
+        return result
+    return wrapper
+
+
 def _create_fn(name, args, body, *, globals=None, locals=None,
                return_type=MISSING):
     # Note that we mutate locals when exec() is called.  Caller
@@ -497,12 +521,13 @@ def _init_fn(fields, frozen, has_post_init, self_name):
 
 
 def _repr_fn(fields):
-    return _create_fn('__repr__',
-                      ('self',),
-                      ['return self.__class__.__qualname__ + f"(' +
-                       ', '.join([f"{f.name}={{self.{f.name}!r}}"
-                                  for f in fields]) +
-                       ')"'])
+    fn = _create_fn('__repr__',
+                    ('self',),
+                    ['return self.__class__.__qualname__ + f"(' +
+                     ', '.join([f"{f.name}={{self.{f.name}!r}}"
+                                for f in fields]) +
+                     ')"'])
+    return _recursive_repr(fn)
 
 
 def _frozen_get_del_attr(cls, fields):
index 12a0f14..a964b20 100644 (file)
@@ -808,9 +808,19 @@ class date:
 
         year, month, day (required, base 1)
         """
-        if month is None and isinstance(year, bytes) and len(year) == 4 and \
-                1 <= year[2] <= 12:
+        if (month is None and
+            isinstance(year, (bytes, str)) and len(year) == 4 and
+            1 <= ord(year[2:3]) <= 12):
             # Pickle support
+            if isinstance(year, str):
+                try:
+                    year = year.encode('latin1')
+                except UnicodeEncodeError:
+                    # More informative error message.
+                    raise ValueError(
+                        "Failed to encode latin1 string when unpickling "
+                        "a date object. "
+                        "pickle.load(data, encoding='latin1') is assumed.")
             self = object.__new__(cls)
             self.__setstate(year)
             self._hashcode = -1
@@ -857,7 +867,7 @@ class date:
             assert len(date_string) == 10
             return cls(*_parse_isoformat_date(date_string))
         except Exception:
-            raise ValueError('Invalid isoformat string: %s' % date_string)
+            raise ValueError(f'Invalid isoformat string: {date_string!r}')
 
 
     # Conversions to string
@@ -1184,8 +1194,18 @@ class time:
         tzinfo (default to None)
         fold (keyword only, default to zero)
         """
-        if isinstance(hour, bytes) and len(hour) == 6 and hour[0]&0x7F < 24:
+        if (isinstance(hour, (bytes, str)) and len(hour) == 6 and
+            ord(hour[0:1])&0x7F < 24):
             # Pickle support
+            if isinstance(hour, str):
+                try:
+                    hour = hour.encode('latin1')
+                except UnicodeEncodeError:
+                    # More informative error message.
+                    raise ValueError(
+                        "Failed to encode latin1 string when unpickling "
+                        "a time object. "
+                        "pickle.load(data, encoding='latin1') is assumed.")
             self = object.__new__(cls)
             self.__setstate(hour, minute or None)
             self._hashcode = -1
@@ -1369,7 +1389,7 @@ class time:
         try:
             return cls(*_parse_isoformat_time(time_string))
         except Exception:
-            raise ValueError('Invalid isoformat string: %s' % time_string)
+            raise ValueError(f'Invalid isoformat string: {time_string!r}')
 
 
     def strftime(self, fmt):
@@ -1496,8 +1516,18 @@ class datetime(date):
 
     def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,
                 microsecond=0, tzinfo=None, *, fold=0):
-        if isinstance(year, bytes) and len(year) == 10 and 1 <= year[2]&0x7F <= 12:
+        if (isinstance(year, (bytes, str)) and len(year) == 10 and
+            1 <= ord(year[2:3])&0x7F <= 12):
             # Pickle support
+            if isinstance(year, str):
+                try:
+                    year = bytes(year, 'latin1')
+                except UnicodeEncodeError:
+                    # More informative error message.
+                    raise ValueError(
+                        "Failed to encode latin1 string when unpickling "
+                        "a datetime object. "
+                        "pickle.load(data, encoding='latin1') is assumed.")
             self = object.__new__(cls)
             self.__setstate(year, month)
             self._hashcode = -1
@@ -1646,13 +1676,13 @@ class datetime(date):
         try:
             date_components = _parse_isoformat_date(dstr)
         except ValueError:
-            raise ValueError('Invalid isoformat string: %s' % date_string)
+            raise ValueError(f'Invalid isoformat string: {date_string!r}')
 
         if tstr:
             try:
                 time_components = _parse_isoformat_time(tstr)
             except ValueError:
-                raise ValueError('Invalid isoformat string: %s' % date_string)
+                raise ValueError(f'Invalid isoformat string: {date_string!r}')
         else:
             time_components = [0, 0, 0, 0, None]
 
index 30b3b47..84b4ef5 100644 (file)
@@ -56,43 +56,43 @@ def _find_vc2015():
     return best_version, best_dir
 
 def _find_vc2017():
-    import _distutils_findvs
-    import threading
+    """Returns "15, path" based on the result of invoking vswhere.exe
+    If no install is found, returns "None, None"
 
-    best_version = 0,   # tuple for full version comparisons
-    best_dir = None
+    The version is returned to avoid unnecessarily changing the function
+    result. It may be ignored when the path is not None.
+
+    If vswhere.exe is not available, by definition, VS 2017 is not
+    installed.
+    """
+    import json
+
+    root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles")
+    if not root:
+        return None, None
 
-    # We need to call findall() on its own thread because it will
-    # initialize COM.
-    all_packages = []
-    def _getall():
-        all_packages.extend(_distutils_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
+        path = subprocess.check_output([
+            os.path.join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"),
+            "-latest",
+            "-prerelease",
+            "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
+            "-property", "installationPath",
+        ], encoding="mbcs", errors="strict").strip()
+    except (subprocess.CalledProcessError, OSError, UnicodeDecodeError):
+        return None, None
+
+    path = os.path.join(path, "VC", "Auxiliary", "Build")
+    if os.path.isdir(path):
+        return 15, path
+
+    return None, None
 
 def _find_vcvarsall(plat_spec):
-    best_version, best_dir = _find_vc2017()
+    _, best_dir = _find_vc2017()
     vcruntime = None
     vcruntime_plat = 'x64' if 'amd64' in plat_spec else 'x86'
-    if best_version:
+    if best_dir:
         vcredist = os.path.join(best_dir, "..", "..", "redist", "MSVC", "**",
             "Microsoft.VC141.CRT", "vcruntime140.dll")
         try:
@@ -101,13 +101,13 @@ def _find_vcvarsall(plat_spec):
         except (ImportError, OSError, LookupError):
             vcruntime = None
 
-    if not best_version:
+    if not best_dir:
         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:
+    if not best_dir:
         log.debug("No suitable Visual C++ version found")
         return None, None
 
index 78ae575..b002dc3 100644 (file)
@@ -166,7 +166,15 @@ def make_zipfile(base_name, base_dir, verbose=0, dry_run=0):
                 zip = zipfile.ZipFile(zip_filename, "w",
                                       compression=zipfile.ZIP_STORED)
 
+            if base_dir != os.curdir:
+                path = os.path.normpath(os.path.join(base_dir, ''))
+                zip.write(path, path)
+                log.info("adding '%s'", path)
             for dirpath, dirnames, filenames in os.walk(base_dir):
+                for name in dirnames:
+                    path = os.path.normpath(os.path.join(dirpath, name, ''))
+                    zip.write(path, path)
+                    log.info("adding '%s'", path)
                 for name in filenames:
                     path = os.path.normpath(os.path.join(dirpath, name))
                     if os.path.isfile(path):
index e9274d9..f0d6b5b 100644 (file)
@@ -32,7 +32,7 @@ class bdist_dumb(Command):
                     ('skip-build', None,
                      "skip rebuilding everything (for testing/debugging)"),
                     ('relative', None,
-                     "build the archive using relative paths"
+                     "build the archive using relative paths "
                      "(default: false)"),
                     ('owner=', 'u',
                      "Owner name used when creating a tar file"
index a4bd5a5..80104c3 100644 (file)
@@ -98,14 +98,14 @@ class bdist_msi(Command):
                     ('no-target-compile', 'c',
                      "do not compile .py to .pyc on the target system"),
                     ('no-target-optimize', 'o',
-                     "do not compile .py to .pyo (optimized)"
+                     "do not compile .py to .pyo (optimized) "
                      "on the target system"),
                     ('dist-dir=', 'd',
                      "directory to put final built distributions in"),
                     ('skip-build', None,
                      "skip rebuilding everything (for testing/debugging)"),
                     ('install-script=', None,
-                     "basename of installation script to be run after"
+                     "basename of installation script to be run after "
                      "installation or before deinstallation"),
                     ('pre-install-script=', None,
                      "Fully qualified filename of a script to be run before "
index ac46217..02f10dd 100644 (file)
@@ -58,7 +58,7 @@ class bdist_rpm(Command):
          "RPM \"vendor\" (eg. \"Joe Blow <joe@example.com>\") "
          "[default: maintainer or author from setup script]"),
         ('packager=', None,
-         "RPM packager (eg. \"Jane Doe <jane@example.net>\")"
+         "RPM packager (eg. \"Jane Doe <jane@example.net>\") "
          "[default: vendor]"),
         ('doc-files=', None,
          "list of documentation files (space or comma-separated)"),
index 0871a4f..fde5675 100644 (file)
@@ -29,7 +29,7 @@ class bdist_wininst(Command):
                     ('no-target-compile', 'c',
                      "do not compile .py to .pyc on the target system"),
                     ('no-target-optimize', 'o',
-                     "do not compile .py to .pyo (optimized)"
+                     "do not compile .py to .pyo (optimized) "
                      "on the target system"),
                     ('dist-dir=', 'd',
                      "directory to put final built distributions in"),
@@ -40,7 +40,7 @@ class bdist_wininst(Command):
                     ('skip-build', None,
                      "skip rebuilding everything (for testing/debugging)"),
                     ('install-script=', None,
-                     "basename of installation script to be run after"
+                     "basename of installation script to be run after "
                      "installation or before deinstallation"),
                     ('pre-install-script=', None,
                      "Fully qualified filename of a script to be run before "
index 8fad9cd..158465d 100644 (file)
@@ -365,7 +365,7 @@ class build_ext(Command):
             ext_name, build_info = ext
 
             log.warn("old-style (ext_name, build_info) tuple found in "
-                     "ext_modules for extension '%s'"
+                     "ext_modules for extension '%s' "
                      "-- please convert to Extension instance", ext_name)
 
             if not (isinstance(ext_name, str) and
index 14ba4ca..e9aad0e 100644 (file)
@@ -122,12 +122,13 @@ class ArchiveUtilTestCase(support.TempdirManager,
         try:
             names = tar.getnames()
             names.sort()
-            return tuple(names)
+            return names
         finally:
             tar.close()
 
-    _created_files = ('dist', 'dist/file1', 'dist/file2',
-                      'dist/sub', 'dist/sub/file3', 'dist/sub2')
+    _zip_created_files = ['dist/', 'dist/file1', 'dist/file2',
+                          'dist/sub/', 'dist/sub/file3', 'dist/sub2/']
+    _created_files = [p.rstrip('/') for p in _zip_created_files]
 
     def _create_files(self):
         # creating something to tar
@@ -244,8 +245,7 @@ class ArchiveUtilTestCase(support.TempdirManager,
         tarball = base_name + '.zip'
         self.assertTrue(os.path.exists(tarball))
         with zipfile.ZipFile(tarball) as zf:
-            self.assertEqual(sorted(zf.namelist()),
-                             ['dist/file1', 'dist/file2', 'dist/sub/file3'])
+            self.assertEqual(sorted(zf.namelist()), self._zip_created_files)
 
     @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run')
     def test_make_zipfile_no_zlib(self):
@@ -271,8 +271,7 @@ class ArchiveUtilTestCase(support.TempdirManager,
                          [((tarball, "w"), {'compression': zipfile.ZIP_STORED})])
         self.assertTrue(os.path.exists(tarball))
         with zipfile.ZipFile(tarball) as zf:
-            self.assertEqual(sorted(zf.namelist()),
-                             ['dist/file1', 'dist/file2', 'dist/sub/file3'])
+            self.assertEqual(sorted(zf.namelist()), self._zip_created_files)
 
     def test_check_archive_formats(self):
         self.assertEqual(check_archive_formats(['gztar', 'xxx', 'zip']),
index c8ccdc2..01a233b 100644 (file)
@@ -84,7 +84,7 @@ class BuildDumbTestCase(support.TempdirManager,
         finally:
             fp.close()
 
-        contents = sorted(os.path.basename(fn) for fn in contents)
+        contents = sorted(filter(None, map(os.path.basename, contents)))
         wanted = ['foo-0.1-py%s.%s.egg-info' % sys.version_info[:2], 'foo.py']
         if not sys.dont_write_bytecode:
             wanted.append('foo.%s.pyc' % sys.implementation.cache_tag)
index 5444b81..23db126 100644 (file)
@@ -128,7 +128,9 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
             zip_file.close()
 
         # making sure everything has been pruned correctly
-        self.assertEqual(len(content), 4)
+        expected = ['', 'PKG-INFO', 'README', 'setup.py',
+                    'somecode/', 'somecode/__init__.py']
+        self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected])
 
     @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run')
     @unittest.skipIf(find_executable('tar') is None,
@@ -226,7 +228,13 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
             zip_file.close()
 
         # making sure everything was added
-        self.assertEqual(len(content), 12)
+        expected = ['', 'PKG-INFO', 'README', 'buildout.cfg',
+                    'data/', 'data/data.dt', 'inroot.txt',
+                    'scripts/', 'scripts/script.py', 'setup.py',
+                    'some/', 'some/file.txt', 'some/other_file.txt',
+                    'somecode/', 'somecode/__init__.py', 'somecode/doc.dat',
+                    'somecode/doc.txt']
+        self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected])
 
         # checking the MANIFEST
         f = open(join(self.tmp_dir, 'MANIFEST'))
index e805a75..416da1a 100644 (file)
@@ -2209,8 +2209,8 @@ def get_section(value):
         digits += value[0]
         value = value[1:]
     if digits[0] == '0' and digits != '0':
-        section.defects.append(errors.InvalidHeaderError("section number"
-            "has an invalid leading 0"))
+        section.defects.append(errors.InvalidHeaderError(
+                "section number has an invalid leading 0"))
     section.number = int(digits)
     section.append(ValueTerminal(digits, 'digits'))
     return section, value
index 4748ba4..09c572d 100644 (file)
@@ -8,9 +8,9 @@ import tempfile
 __all__ = ["version", "bootstrap"]
 
 
-_SETUPTOOLS_VERSION = "39.0.1"
+_SETUPTOOLS_VERSION = "40.6.2"
 
-_PIP_VERSION = "10.0.1"
+_PIP_VERSION = "18.1"
 
 _PROJECTS = [
     ("setuptools", _SETUPTOOLS_VERSION),
diff --git a/Lib/ensurepip/_bundled/pip-10.0.1-py2.py3-none-any.whl b/Lib/ensurepip/_bundled/pip-10.0.1-py2.py3-none-any.whl
deleted file mode 100644 (file)
index 9837092..0000000
Binary files a/Lib/ensurepip/_bundled/pip-10.0.1-py2.py3-none-any.whl and /dev/null differ
diff --git a/Lib/ensurepip/_bundled/pip-18.1-py2.py3-none-any.whl b/Lib/ensurepip/_bundled/pip-18.1-py2.py3-none-any.whl
new file mode 100644 (file)
index 0000000..c3c146f
Binary files /dev/null and b/Lib/ensurepip/_bundled/pip-18.1-py2.py3-none-any.whl differ
similarity index 54%
rename from Lib/ensurepip/_bundled/setuptools-39.0.1-py2.py3-none-any.whl
rename to Lib/ensurepip/_bundled/setuptools-40.6.2-py2.py3-none-any.whl
index edc3ca2..4c8a619 100644 (file)
Binary files a/Lib/ensurepip/_bundled/setuptools-39.0.1-py2.py3-none-any.whl and b/Lib/ensurepip/_bundled/setuptools-40.6.2-py2.py3-none-any.whl differ
index 87f3691..e5a80cd 100644 (file)
@@ -540,7 +540,25 @@ class Enum(metaclass=EnumMeta):
                 if member._value_ == value:
                     return member
         # still not found -- try _missing_ hook
-        return cls._missing_(value)
+        try:
+            exc = None
+            result = cls._missing_(value)
+        except Exception as e:
+            exc = e
+            result = None
+        if isinstance(result, cls):
+            return result
+        else:
+            ve_exc = ValueError("%r is not a valid %s" % (value, cls.__name__))
+            if result is None and exc is None:
+                raise ve_exc
+            elif exc is None:
+                exc = TypeError(
+                        'error in %s._missing_: returned %r instead of None or a valid member'
+                        % (cls.__name__, result)
+                        )
+            exc.__context__ = ve_exc
+            raise exc
 
     def _generate_next_value_(name, start, count, last_values):
         for last_value in reversed(last_values):
index 05840d4..9611282 100644 (file)
@@ -732,7 +732,7 @@ else:
                                  "exclusive")
             if keyfile is not None or certfile is not None:
                 import warnings
-                warnings.warn("keyfile and certfile are deprecated, use a"
+                warnings.warn("keyfile and certfile are deprecated, use a "
                               "custom context instead", DeprecationWarning, 2)
             self.keyfile = keyfile
             self.certfile = certfile
index 133807c..b93769e 100644 (file)
@@ -1,8 +1,51 @@
-What's New in IDLE 3.7.1
+What's New in IDLE 3.7.2
 Released on 2018-07-31?
 ======================================
 
 
+bpo-34864: When starting IDLE on MacOS, warn if the system setting
+"Prefer tabs when opening documents" is "Always".  As previous
+documented for this issue, running IDLE with this setting causes
+problems.  If the setting is changed while IDLE is running,
+there will be no warning until IDLE is restarted.
+
+bpo-35213: Where appropriate, use 'macOS' in idlelib.
+
+bpo-34864: Document two IDLE on MacOS issues.  The System Preferences
+Dock "prefer tabs always" setting disables some IDLE features.
+Menus are a bit different than as described for Windows and Linux.
+
+bpo-35202: Remove unused imports in idlelib.
+
+bpo-33000: Document that IDLE's shell has no line limit.
+A program that runs indefinitely can overfill memory.
+
+bpo-23220: Explain how IDLE's Shell displays output.
+Add new subsection "User output in Shell".
+
+bpo-35099: Improve the doc about IDLE running user code.
+"IDLE -- console differences" is renamed "Running user code".
+It mostly covers the implications of using custom sys.stdxxx objects.
+
+bpo-35097: Add IDLE doc subsection explaining editor windows.
+Topics include opening, title and status bars, .py* extension, and running.
+
+Issue 35093: Document the IDLE document viewer in the IDLE doc.
+Add a paragraph in "Help and preferences", "Help sources" subsection.
+
+bpo-1529353: Explain Shell text squeezing in the IDLE doc.
+
+bpo-35088: Update idlelib.help.copy_string docstring.
+We now use git and backporting instead of hg and forward merging.
+
+bpo-35087: Update idlelib help files for the current doc build.
+The main change is the elimination of chapter-section numbers.
+
+
+What's New in IDLE 3.7.1
+Released on 2018-07-31?
+======================================
+
 bpo-1529353: Output over N lines (50 by default) is squeezed down to a button.
 N can be changed in the PyShell section of the General page of the
 Settings dialog.  Fewer, but possibly extra long, lines can be squeezed by
index 7c88a4d..ef88528 100644 (file)
@@ -13,7 +13,7 @@ import re
 from sys import maxsize as INFINITY
 
 import tkinter
-from tkinter.constants import TOP, LEFT, X, W, SUNKEN
+from tkinter.constants import TOP, X, SUNKEN
 
 from idlelib.config import idleConf
 
index 0eb90fc..79d988f 100644 (file)
@@ -562,12 +562,11 @@ class IdleConf:
         result = self.GetKeySet(self.CurrentKeys())
 
         if sys.platform == "darwin":
-            # OS X Tk variants do not support the "Alt" keyboard modifier.
-            # So replace all keybingings that use "Alt" with ones that
-            # use the "Option" keyboard modifier.
-            # TODO (Ned?): the "Option" modifier does not work properly for
-            #        Cocoa Tk and XQuartz Tk so we should not use it
-            #        in default OS X KeySets.
+            # macOS (OS X) Tk variants do not support the "Alt"
+            # keyboard modifier.  Replace it with "Option".
+            # TODO (Ned?): the "Option" modifier does not work properly
+            #     for Cocoa Tk and XQuartz Tk so we should not use it
+            #     in the default 'OSX' keyset.
             for k, v in result.items():
                 v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
                 if v != v2:
index 01a3bd2..0e6dcfb 100644 (file)
@@ -157,7 +157,7 @@ class IdbAdapter:
     #----------called by a DictProxy----------
 
     def dict_keys(self, did):
-        raise NotImplemented("dict_keys not public or pickleable")
+        raise NotImplementedError("dict_keys not public or pickleable")
 ##         dict = dicttable[did]
 ##         return dict.keys()
 
index 5262839..0d20085 100644 (file)
@@ -115,7 +115,6 @@ def _test():  # TODO check and convert to htest
     from tkinter import Tk
     from idlelib.editor import fixwordbreaks
     from idlelib.run import fix_scaling
-    import sys
     root = Tk()
     fix_scaling(root)
     fixwordbreaks(root)
index 5b4d093..e2bf773 100644 (file)
@@ -6,14 +6,18 @@
   <head>
     <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-    <title>26.5. IDLE &#8212; Python 3.8.0a0 documentation</title>
+    <title>IDLE &#8212; Python 3.8.0a0 documentation</title>
     <link rel="stylesheet" href="../_static/pydoctheme.css" type="text/css" />
     <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
-    <script type="text/javascript" src="../_static/documentation_options.js"></script>
+
+    <script type="text/javascript" id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
     <script type="text/javascript" src="../_static/jquery.js"></script>
     <script type="text/javascript" src="../_static/underscore.js"></script>
     <script type="text/javascript" src="../_static/doctools.js"></script>
+    <script async="async" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
+
     <script type="text/javascript" src="../_static/sidebar.js"></script>
+
     <link rel="search" type="application/opensearchdescription+xml"
           title="Search within Python 3.8.0a0 documentation"
           href="../_static/opensearch.xml"/>
     <link rel="index" title="Index" href="../genindex.html" />
     <link rel="search" title="Search" href="../search.html" />
     <link rel="copyright" title="Copyright" href="../copyright.html" />
-    <link rel="next" title="26.6. Other Graphical User Interface Packages" href="othergui.html" />
-    <link rel="prev" title="26.4. tkinter.scrolledtext — Scrolled Text Widget" href="tkinter.scrolledtext.html" />
+    <link rel="next" title="Other Graphical User Interface Packages" href="othergui.html" />
+    <link rel="prev" title="tkinter.scrolledtext — Scrolled Text Widget" href="tkinter.scrolledtext.html" />
     <link rel="canonical" href="https://docs.python.org/3/library/idle.html" />
 
 
 
 
 
+
+    <style>
+      @media only screen {
+        table.full-width-table {
+            width: 100%;
+        }
+      }
+    </style>
+
     <link rel="shortcut icon" type="image/png" href="../_static/py.png" />
 
     <script type="text/javascript" src="../_static/copybutton.js"></script>
           <a href="../py-modindex.html" title="Python Module Index"
              >modules</a> |</li>
         <li class="right" >
-          <a href="othergui.html" title="26.6. Other Graphical User Interface Packages"
+          <a href="othergui.html" title="Other Graphical User Interface Packages"
              accesskey="N">next</a> |</li>
         <li class="right" >
-          <a href="tkinter.scrolledtext.html" title="26.4. tkinter.scrolledtext — Scrolled Text Widget"
+          <a href="tkinter.scrolledtext.html" title="tkinter.scrolledtext — Scrolled Text Widget"
              accesskey="P">previous</a> |</li>
 
     <li><img src="../_static/py.png" alt=""
@@ -63,7 +76,7 @@
     </li>
 
           <li class="nav-item nav-item-1"><a href="index.html" >The Python Standard Library</a> &#187;</li>
-          <li class="nav-item nav-item-2"><a href="tk.html" accesskey="U">26. Graphical User Interfaces with Tk</a> &#187;</li>
+          <li class="nav-item nav-item-2"><a href="tk.html" accesskey="U">Graphical User Interfaces with Tk</a> &#187;</li>
     <li class="right">
 
 
           <div class="body" role="main">
 
   <div class="section" id="idle">
-<span id="id1"></span><h1>26.5. IDLE<a class="headerlink" href="#idle" title="Permalink to this headline">¶</a></h1>
+<span id="id1"></span><h1>IDLE<a class="headerlink" href="#idle" title="Permalink to this headline">¶</a></h1>
 <p><strong>Source code:</strong> <a class="reference external" href="https://github.com/python/cpython/tree/master/Lib/idlelib/">Lib/idlelib/</a></p>
 <hr class="docutils" id="index-0" />
 <p>IDLE is Python’s Integrated Development and Learning Environment.</p>
 <p>IDLE has the following features:</p>
 <ul class="simple">
 <li>coded in 100% pure Python, using the <a class="reference internal" href="tkinter.html#module-tkinter" title="tkinter: Interface to Tcl/Tk for graphical user interfaces"><code class="xref py py-mod docutils literal notranslate"><span class="pre">tkinter</span></code></a> GUI toolkit</li>
-<li>cross-platform: works mostly the same on Windows, Unix, and Mac OS X</li>
+<li>cross-platform: works mostly the same on Windows, Unix, and macOS</li>
 <li>Python shell window (interactive interpreter) with colorizing
 of code input, output, and error messages</li>
 <li>multi-window text editor with multiple undo, Python colorizing,
@@ -107,16 +120,19 @@ of global and local namespaces</li>
 <li>configuration, browsers, and other dialogs</li>
 </ul>
 <div class="section" id="menus">
-<h2>26.5.1. Menus<a class="headerlink" href="#menus" title="Permalink to this headline">¶</a></h2>
+<h2>Menus<a class="headerlink" href="#menus" title="Permalink to this headline">¶</a></h2>
 <p>IDLE has two main window types, the Shell window and the Editor window.  It is
-possible to have multiple editor windows simultaneously.  Output windows, such
-as used for Edit / Find in Files, are a subtype of edit window.  They currently
-have the same top menu as Editor windows but a different default title and
-context menu.</p>
-<p>IDLE’s menus dynamically change based on which window is currently selected.
-Each menu documented below indicates which window type it is associated with.</p>
+possible to have multiple editor windows simultaneously.  On Windows and
+Linux, each has its own top menu.  Each menu documented below indicates
+which window type it is associated with.</p>
+<p>Output windows, such as used for Edit =&gt; Find in Files, are a subtype of editor
+window.  They currently have the same top menu but a different
+default title and context menu.</p>
+<p>On macOS, there is one application menu.  It dynamically changes according
+to the window currently selected.  It has an IDLE menu, and some entries
+described below are moved around to conform to Apple guidlines.</p>
 <div class="section" id="file-menu-shell-and-editor">
-<h3>26.5.1.1. File menu (Shell and Editor)<a class="headerlink" href="#file-menu-shell-and-editor" title="Permalink to this headline">¶</a></h3>
+<h3>File menu (Shell and Editor)<a class="headerlink" href="#file-menu-shell-and-editor" title="Permalink to this headline">¶</a></h3>
 <dl class="docutils">
 <dt>New File</dt>
 <dd>Create a new file editing window.</dd>
@@ -154,7 +170,7 @@ file.</dd>
 </dl>
 </div>
 <div class="section" id="edit-menu-shell-and-editor">
-<h3>26.5.1.2. Edit menu (Shell and Editor)<a class="headerlink" href="#edit-menu-shell-and-editor" title="Permalink to this headline">¶</a></h3>
+<h3>Edit menu (Shell and Editor)<a class="headerlink" href="#edit-menu-shell-and-editor" title="Permalink to this headline">¶</a></h3>
 <dl class="docutils">
 <dt>Undo</dt>
 <dd>Undo the last change to the current window.  A maximum of 1000 changes may
@@ -198,7 +214,7 @@ function parameter hints.</dd>
 </dl>
 </div>
 <div class="section" id="format-menu-editor-window-only">
-<h3>26.5.1.3. Format menu (Editor window only)<a class="headerlink" href="#format-menu-editor-window-only" title="Permalink to this headline">¶</a></h3>
+<h3>Format menu (Editor window only)<a class="headerlink" href="#format-menu-editor-window-only" title="Permalink to this headline">¶</a></h3>
 <dl class="docutils">
 <dt>Indent Region</dt>
 <dd>Shift selected lines right by the indent width (default 4 spaces).</dd>
@@ -229,7 +245,7 @@ including lines within multiline strings.</dd>
 </dl>
 </div>
 <div class="section" id="run-menu-editor-window-only">
-<span id="index-2"></span><h3>26.5.1.4. Run menu (Editor window only)<a class="headerlink" href="#run-menu-editor-window-only" title="Permalink to this headline">¶</a></h3>
+<span id="index-2"></span><h3>Run menu (Editor window only)<a class="headerlink" href="#run-menu-editor-window-only" title="Permalink to this headline">¶</a></h3>
 <dl class="docutils">
 <dt>Python Shell</dt>
 <dd>Open or wake up the Python Shell window.</dd>
@@ -250,7 +266,7 @@ line.</dd>
 </dl>
 </div>
 <div class="section" id="shell-menu-shell-window-only">
-<h3>26.5.1.5. Shell menu (Shell window only)<a class="headerlink" href="#shell-menu-shell-window-only" title="Permalink to this headline">¶</a></h3>
+<h3>Shell menu (Shell window only)<a class="headerlink" href="#shell-menu-shell-window-only" title="Permalink to this headline">¶</a></h3>
 <dl class="docutils">
 <dt>View Last Restart</dt>
 <dd>Scroll the shell window to the last Shell restart.</dd>
@@ -261,7 +277,7 @@ line.</dd>
 </dl>
 </div>
 <div class="section" id="debug-menu-shell-window-only">
-<h3>26.5.1.6. Debug menu (Shell window only)<a class="headerlink" href="#debug-menu-shell-window-only" title="Permalink to this headline">¶</a></h3>
+<h3>Debug menu (Shell window only)<a class="headerlink" href="#debug-menu-shell-window-only" title="Permalink to this headline">¶</a></h3>
 <dl class="docutils">
 <dt>Go to File/Line</dt>
 <dd>Look on the current line. with the cursor, and the line above for a filename
@@ -283,12 +299,12 @@ access to locals and globals.</dd>
 </dl>
 </div>
 <div class="section" id="options-menu-shell-and-editor">
-<h3>26.5.1.7. Options menu (Shell and Editor)<a class="headerlink" href="#options-menu-shell-and-editor" title="Permalink to this headline">¶</a></h3>
+<h3>Options menu (Shell and Editor)<a class="headerlink" href="#options-menu-shell-and-editor" title="Permalink to this headline">¶</a></h3>
 <dl class="docutils">
 <dt>Configure IDLE</dt>
 <dd><p class="first">Open a configuration dialog and change preferences for the following:
 fonts, indentation, keybindings, text color themes, startup windows and
-size, additional help sources, and extensions (see below).  On OS X,
+size, additional help sources, and extensions (see below).  On macOS,
 open the configuration dialog by selecting Preferences in the application
 menu.  To use a new built-in color theme (IDLE Dark) with older IDLEs,
 save it as a new custom theme.</p>
@@ -303,7 +319,7 @@ line in this pane exposes that line at the top of the editor.</dd>
 </dl>
 </div>
 <div class="section" id="window-menu-shell-and-editor">
-<h3>26.5.1.8. Window menu (Shell and Editor)<a class="headerlink" href="#window-menu-shell-and-editor" title="Permalink to this headline">¶</a></h3>
+<h3>Window menu (Shell and Editor)<a class="headerlink" href="#window-menu-shell-and-editor" title="Permalink to this headline">¶</a></h3>
 <dl class="docutils">
 <dt>Zoom Height</dt>
 <dd>Toggles the window between normal size and maximum height. The initial size
@@ -314,25 +330,26 @@ Configure IDLE dialog.</dd>
 it to the foreground (deiconifying it if necessary).</p>
 </div>
 <div class="section" id="help-menu-shell-and-editor">
-<h3>26.5.1.9. Help menu (Shell and Editor)<a class="headerlink" href="#help-menu-shell-and-editor" title="Permalink to this headline">¶</a></h3>
+<h3>Help menu (Shell and Editor)<a class="headerlink" href="#help-menu-shell-and-editor" title="Permalink to this headline">¶</a></h3>
 <dl class="docutils">
 <dt>About IDLE</dt>
 <dd>Display version, copyright, license, credits, and more.</dd>
 <dt>IDLE Help</dt>
-<dd>Display a help file for IDLE detailing the menu options, basic editing and
+<dd>Display this IDLE document, detailing the menu options, basic editing and
 navigation, and other tips.</dd>
 <dt>Python Docs</dt>
 <dd>Access local Python documentation, if installed, or start a web browser
 and open docs.python.org showing the latest Python documentation.</dd>
 <dt>Turtle Demo</dt>
-<dd>Run the turtledemo module with example python code and turtle drawings.</dd>
+<dd>Run the turtledemo module with example Python code and turtle drawings.</dd>
 </dl>
 <p>Additional help sources may be added here with the Configure IDLE dialog under
-the General tab.</p>
+the General tab. See the “Help sources” subsection below for more
+on Help menu choices.</p>
 </div>
 <div class="section" id="context-menus">
-<span id="index-4"></span><h3>26.5.1.10. Context Menus<a class="headerlink" href="#context-menus" title="Permalink to this headline">¶</a></h3>
-<p>Open a context menu by right-clicking in a window (Control-click on OS X).
+<span id="index-4"></span><h3>Context Menus<a class="headerlink" href="#context-menus" title="Permalink to this headline">¶</a></h3>
+<p>Open a context menu by right-clicking in a window (Control-click on macOS).
 Context menus have the standard clipboard functions also on the Edit menu.</p>
 <dl class="docutils">
 <dt>Cut</dt>
@@ -351,17 +368,38 @@ debugger.  Breakpoints for a file are saved in the user’s .idlerc directory.</
 <dt>Clear Breakpoint</dt>
 <dd>Clear the breakpoint on that line.</dd>
 </dl>
-<p>Shell and Output windows have the following.</p>
+<p>Shell and Output windows also have the following.</p>
 <dl class="docutils">
 <dt>Go to file/line</dt>
 <dd>Same as in Debug menu.</dd>
 </dl>
+<p>The Shell window also has an output squeezing facility explained in the
+the <em>Python Shell window</em> subsection below.</p>
+<dl class="docutils">
+<dt>Squeeze</dt>
+<dd>If the cursor is over an output line, squeeze all the output between
+the code above and the prompt below down to a ‘Squeezed text’ label.</dd>
+</dl>
 </div>
 </div>
 <div class="section" id="editing-and-navigation">
-<h2>26.5.2. Editing and navigation<a class="headerlink" href="#editing-and-navigation" title="Permalink to this headline">¶</a></h2>
+<h2>Editing and navigation<a class="headerlink" href="#editing-and-navigation" title="Permalink to this headline">¶</a></h2>
+<div class="section" id="editor-windows">
+<h3>Editor windows<a class="headerlink" href="#editor-windows" title="Permalink to this headline">¶</a></h3>
+<p>IDLE may open editor windows when it starts, depending on settings
+and how you start IDLE.  Thereafter, use the File menu.  There can be only
+one open editor window for a given file.</p>
+<p>The title bar contains the name of the file, the full path, and the version
+of Python and IDLE running the window.  The status bar contains the line
+number (‘Ln’) and column number (‘Col’).  Line numbers start with 1;
+column numbers with 0.</p>
+<p>IDLE assumes that files with a known .py* extension contain Python code
+and that other files do not.  Run Python code with the Run menu.</p>
+</div>
+<div class="section" id="key-bindings">
+<h3>Key bindings<a class="headerlink" href="#key-bindings" title="Permalink to this headline">¶</a></h3>
 <p>In this section, ‘C’ refers to the <kbd class="kbd docutils literal notranslate">Control</kbd> key on Windows and Unix and
-the <kbd class="kbd docutils literal notranslate">Command</kbd> key on Mac OSX.</p>
+the <kbd class="kbd docutils literal notranslate">Command</kbd> key on macOS.</p>
 <ul>
 <li><p class="first"><kbd class="kbd docutils literal notranslate">Backspace</kbd> deletes to the left; <kbd class="kbd docutils literal notranslate">Del</kbd> deletes to the right</p>
 </li>
@@ -395,8 +433,9 @@ this)</li>
 </ul>
 <p>Standard keybindings (like <kbd class="kbd docutils literal notranslate">C-c</kbd> to copy and <kbd class="kbd docutils literal notranslate">C-v</kbd> to paste)
 may work.  Keybindings are selected in the Configure IDLE dialog.</p>
+</div>
 <div class="section" id="automatic-indentation">
-<h3>26.5.2.1. Automatic indentation<a class="headerlink" href="#automatic-indentation" title="Permalink to this headline">¶</a></h3>
+<h3>Automatic indentation<a class="headerlink" href="#automatic-indentation" title="Permalink to this headline">¶</a></h3>
 <p>After a block-opening statement, the next line is indented by 4 spaces (in the
 Python Shell window by one tab).  After certain keywords (break, return etc.)
 the next line is dedented.  In leading indentation, <kbd class="kbd docutils literal notranslate">Backspace</kbd> deletes up
@@ -406,7 +445,7 @@ are restricted to four spaces due to Tcl/Tk limitations.</p>
 <p>See also the indent/dedent region commands in the edit menu.</p>
 </div>
 <div class="section" id="completions">
-<h3>26.5.2.2. Completions<a class="headerlink" href="#completions" title="Permalink to this headline">¶</a></h3>
+<h3>Completions<a class="headerlink" href="#completions" title="Permalink to this headline">¶</a></h3>
 <p>Completions are supplied for functions, classes, and attributes of classes,
 both built-in and user-defined. Completions are also provided for
 filenames.</p>
@@ -441,7 +480,7 @@ much can be found by default, e.g. the re module.</p>
 longer or disable the extension.</p>
 </div>
 <div class="section" id="calltips">
-<h3>26.5.2.3. Calltips<a class="headerlink" href="#calltips" title="Permalink to this headline">¶</a></h3>
+<h3>Calltips<a class="headerlink" href="#calltips" title="Permalink to this headline">¶</a></h3>
 <p>A calltip is shown when one types <kbd class="kbd docutils literal notranslate">(</kbd> after the name of an <em>accessible</em>
 function.  A name expression may include dots and subscripts.  A calltip
 remains until it is clicked, the cursor is moved out of the argument area,
@@ -464,7 +503,15 @@ might want to run a file after writing the import statements at the top,
 or immediately run an existing file before editing.</p>
 </div>
 <div class="section" id="python-shell-window">
-<h3>26.5.2.4. Python Shell window<a class="headerlink" href="#python-shell-window" title="Permalink to this headline">¶</a></h3>
+<h3>Python Shell window<a class="headerlink" href="#python-shell-window" title="Permalink to this headline">¶</a></h3>
+<p>With IDLE’s Shell, one enters, edits, and recalls complete statements.
+Most consoles and terminals only work with a single physical line at a time.</p>
+<p>When one pastes code into Shell, it is not compiled and possibly executed
+until one hits <kbd class="kbd docutils literal notranslate">Return</kbd>.  One may edit pasted code first.
+If one pastes more that one statement into Shell, the result will be a
+<a class="reference internal" href="exceptions.html#SyntaxError" title="SyntaxError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">SyntaxError</span></code></a> when multiple statements are compiled as if they were one.</p>
+<p>The editing features described in previous subsections work when entering
+code interactively.  IDLE’s Shell window also responds to the following keys.</p>
 <ul>
 <li><p class="first"><kbd class="kbd docutils literal notranslate">C-c</kbd> interrupts executing command</p>
 </li>
@@ -474,15 +521,15 @@ or immediately run an existing file before editing.</p>
 <p>Command history</p>
 <ul class="simple">
 <li><kbd class="kbd docutils literal notranslate">Alt-p</kbd> retrieves previous command matching what you have typed. On
-OS X use <kbd class="kbd docutils literal notranslate">C-p</kbd>.</li>
-<li><kbd class="kbd docutils literal notranslate">Alt-n</kbd> retrieves next. On OS X use <kbd class="kbd docutils literal notranslate">C-n</kbd>.</li>
+macOS use <kbd class="kbd docutils literal notranslate">C-p</kbd>.</li>
+<li><kbd class="kbd docutils literal notranslate">Alt-n</kbd> retrieves next. On macOS use <kbd class="kbd docutils literal notranslate">C-n</kbd>.</li>
 <li><kbd class="kbd docutils literal notranslate">Return</kbd> while on any previous command retrieves that command</li>
 </ul>
 </li>
 </ul>
 </div>
 <div class="section" id="text-colors">
-<h3>26.5.2.5. Text colors<a class="headerlink" href="#text-colors" title="Permalink to this headline">¶</a></h3>
+<h3>Text colors<a class="headerlink" href="#text-colors" title="Permalink to this headline">¶</a></h3>
 <p>Idle defaults to black on white text, but colors text with special meanings.
 For the shell, these are shell output, shell error, user output, and
 user error.  For Python code, at the shell prompt or in an editor, these are
@@ -496,7 +543,7 @@ text in popups and dialogs is not user-configurable.</p>
 </div>
 </div>
 <div class="section" id="startup-and-code-execution">
-<h2>26.5.3. Startup and code execution<a class="headerlink" href="#startup-and-code-execution" title="Permalink to this headline">¶</a></h2>
+<h2>Startup and code execution<a class="headerlink" href="#startup-and-code-execution" title="Permalink to this headline">¶</a></h2>
 <p>Upon startup with the <code class="docutils literal notranslate"><span class="pre">-s</span></code> option, IDLE will execute the file referenced by
 the environment variables <span class="target" id="index-5"></span><code class="xref std std-envvar docutils literal notranslate"><span class="pre">IDLESTARTUP</span></code> or <span class="target" id="index-6"></span><a class="reference internal" href="../using/cmdline.html#envvar-PYTHONSTARTUP"><code class="xref std std-envvar docutils literal notranslate"><span class="pre">PYTHONSTARTUP</span></code></a>.
 IDLE first checks for <code class="docutils literal notranslate"><span class="pre">IDLESTARTUP</span></code>; if <code class="docutils literal notranslate"><span class="pre">IDLESTARTUP</span></code> is present the file
@@ -510,7 +557,7 @@ looked for in the user’s home directory.  Statements in this file will be
 executed in the Tk namespace, so this file is not useful for importing
 functions to be used from IDLE’s Python shell.</p>
 <div class="section" id="command-line-usage">
-<h3>26.5.3.1. Command line usage<a class="headerlink" href="#command-line-usage" title="Permalink to this headline">¶</a></h3>
+<h3>Command line usage<a class="headerlink" href="#command-line-usage" title="Permalink to this headline">¶</a></h3>
 <div class="highlight-none notranslate"><div class="highlight"><pre><span></span>idle.py [-c command] [-d] [-e] [-h] [-i] [-r file] [-s] [-t title] [-] [arg] ...
 
 -c command  run command in the shell window
@@ -535,7 +582,7 @@ set in the Options dialog.</li>
 </ul>
 </div>
 <div class="section" id="startup-failure">
-<h3>26.5.3.2. Startup failure<a class="headerlink" href="#startup-failure" title="Permalink to this headline">¶</a></h3>
+<h3>Startup failure<a class="headerlink" href="#startup-failure" title="Permalink to this headline">¶</a></h3>
 <p>IDLE uses a socket to communicate between the IDLE GUI process and the user
 code execution process.  A connection must be established whenever the Shell
 starts or restarts.  (The latter is indicated by a divider line that says
@@ -570,26 +617,75 @@ be to delete one or more of the configuration files.</p>
 <p>If IDLE quits with no message, and it was not started from a console, try
 starting from a console (<code class="docutils literal notranslate"><span class="pre">python</span> <span class="pre">-m</span> <span class="pre">idlelib)</span></code> and see if a message appears.</p>
 </div>
-<div class="section" id="idle-console-differences">
-<h3>26.5.3.3. IDLE-console differences<a class="headerlink" href="#idle-console-differences" title="Permalink to this headline">¶</a></h3>
+<div class="section" id="running-user-code">
+<h3>Running user code<a class="headerlink" href="#running-user-code" title="Permalink to this headline">¶</a></h3>
 <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.
+intended to be the same as executing the same code by the default method,
+directly with Python in a text-mode system console or terminal window.
 However, the different interface and operation occasionally affect
-visible results.  For instance, <code class="docutils literal notranslate"><span class="pre">sys.modules</span></code> starts with more entries.</p>
-<p>IDLE also replaces <code class="docutils literal notranslate"><span class="pre">sys.stdin</span></code>, <code class="docutils literal notranslate"><span class="pre">sys.stdout</span></code>, and <code class="docutils literal notranslate"><span class="pre">sys.stderr</span></code> with
-objects that get input from and send output to the Shell window.
-When Shell has the focus, it controls the keyboard and screen.  This is
+visible results.  For instance, <code class="docutils literal notranslate"><span class="pre">sys.modules</span></code> starts with more entries,
+and <code class="docutils literal notranslate"><span class="pre">threading.activeCount()</span></code> returns 2 instead of 1.</p>
+<p>By default, IDLE runs user code in a separate OS process rather than in
+the user interface process that runs the shell and editor.  In the execution
+process, it replaces <code class="docutils literal notranslate"><span class="pre">sys.stdin</span></code>, <code class="docutils literal notranslate"><span class="pre">sys.stdout</span></code>, and <code class="docutils literal notranslate"><span class="pre">sys.stderr</span></code>
+with objects that get input from and send output to the Shell window.
+The original values stored in <code class="docutils literal notranslate"><span class="pre">sys.__stdin__</span></code>, <code class="docutils literal notranslate"><span class="pre">sys.__stdout__</span></code>, and
+<code class="docutils literal notranslate"><span class="pre">sys.__stderr__</span></code> are not touched, but may be <code class="docutils literal notranslate"><span class="pre">None</span></code>.</p>
+<p>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 notranslate"><span class="pre">sys</span></code> is reset with <code class="docutils literal notranslate"><span class="pre">importlib.reload(sys)</span></code>,
-IDLE’s changes are lost and things like <code class="docutils literal notranslate"><span class="pre">input</span></code>, <code class="docutils literal notranslate"><span class="pre">raw_input</span></code>, and
-<code class="docutils literal notranslate"><span class="pre">print</span></code> will not work correctly.</p>
-<p>With IDLE’s Shell, one enters, edits, and recalls complete statements.
-Some consoles only work with a single physical line at a time.  IDLE uses
-<code class="docutils literal notranslate"><span class="pre">exec</span></code> to run each statement.  As a result, <code class="docutils literal notranslate"><span class="pre">'__builtins__'</span></code> is always
-defined for each statement.</p>
+and screen will not work.  These include system-specific functions that
+determine whether a key has been pressed and if so, which.</p>
+<p>IDLE’s standard stream replacements are not inherited by subprocesses
+created in the execution process, whether directly by user code or by modules
+such as multiprocessing.  If such subprocess use <code class="docutils literal notranslate"><span class="pre">input</span></code> from sys.stdin
+or <code class="docutils literal notranslate"><span class="pre">print</span></code> or <code class="docutils literal notranslate"><span class="pre">write</span></code> to sys.stdout or sys.stderr,
+IDLE should be started in a command line window.  The secondary subprocess
+will then be attached to that window for input and output.</p>
+<p>If <code class="docutils literal notranslate"><span class="pre">sys</span></code> is reset by user code, such as with <code class="docutils literal notranslate"><span class="pre">importlib.reload(sys)</span></code>,
+IDLE’s changes are lost and input from the keyboard and output to the screen
+will not work correctly.</p>
+</div>
+<div class="section" id="user-output-in-shell">
+<h3>User output in Shell<a class="headerlink" href="#user-output-in-shell" title="Permalink to this headline">¶</a></h3>
+<p>When a program outputs text, the result is determined by the
+corresponding output device.  When IDLE executes user code, <code class="docutils literal notranslate"><span class="pre">sys.stdout</span></code>
+and <code class="docutils literal notranslate"><span class="pre">sys.stderr</span></code> are connected to the display area of IDLE’s Shell.  Some of
+its features are inherited from the underlying Tk Text widget.  Others
+are programmed additions.  Where it matters, Shell is designed for development
+rather than production runs.</p>
+<p>For instance, Shell never throws away output.  A program that sends unlimited
+output to Shell will eventually fill memory, resulting in a memory error.
+In contrast, some system text windows only keep the last n lines of output.
+A Windows console, for instance, keeps a user-settable 1 to 9999 lines,
+with 300 the default.</p>
+<p>Text widgets display a subset of Unicode, the Basic Multilingual Plane (BMP).
+Which characters get a proper glyph instead of a replacement box depends on
+the operating system and installed fonts.  Newline characters cause following
+text to appear on a new line, but other control characters are either
+replaced with a box or deleted.  However, <code class="docutils literal notranslate"><span class="pre">repr()</span></code>, which is used for
+interactive echo of expression values, replaces control characters,
+some BMP codepoints, and all non-BMP characters with escape codes
+before they are output.</p>
+<p>Normal and error output are generally kept separate (on separate lines)
+from code input and each other.  They each get different highlight colors.</p>
+<p>For SyntaxError tracebacks, the normal ‘^’ marking where the error was
+detected is replaced by coloring the text with an error highlight.
+When code run from a file causes other exceptions, one may right click
+on a traceback line to jump to the corresponding line in an IDLE editor.
+The file will be opened if necessary.</p>
+<p>Shell has a special facility for squeezing output lines down to a
+‘Squeezed text’ label.  This is done automatically
+for output over N lines (N = 50 by default).
+N can be changed in the PyShell section of the General
+page of the Settings dialog.  Output with fewer lines can be squeezed by
+right clicking on the output.  This can be useful lines long enough to slow
+down scrolling.</p>
+<p>Squeezed output is expanded in place by double-clicking the label.
+It can also be sent to the clipboard or a separate view window by
+right-clicking the label.</p>
 </div>
 <div class="section" id="developing-tkinter-applications">
-<h3>26.5.3.4. Developing tkinter applications<a class="headerlink" href="#developing-tkinter-applications" title="Permalink to this headline">¶</a></h3>
+<h3>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 notranslate"><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
@@ -609,7 +705,7 @@ 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>26.5.3.5. Running without a subprocess<a class="headerlink" href="#running-without-a-subprocess" title="Permalink to this headline">¶</a></h3>
+<h3>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,24 +731,39 @@ with the default subprocess if at all possible.</p>
 </div>
 </div>
 <div class="section" id="help-and-preferences">
-<h2>26.5.4. Help and preferences<a class="headerlink" href="#help-and-preferences" title="Permalink to this headline">¶</a></h2>
-<div class="section" id="additional-help-sources">
-<h3>26.5.4.1. Additional help sources<a class="headerlink" href="#additional-help-sources" title="Permalink to this headline">¶</a></h3>
-<p>IDLE includes a help menu entry called “Python Docs” that will open the
-extensive sources of help, including tutorials, available at docs.python.org.
-Selected URLs can be added or removed from the help menu at any time using the
-Configure IDLE dialog. See the IDLE help option in the help menu of IDLE for
-more information.</p>
+<h2>Help and preferences<a class="headerlink" href="#help-and-preferences" title="Permalink to this headline">¶</a></h2>
+<div class="section" id="help-sources">
+<h3>Help sources<a class="headerlink" href="#help-sources" title="Permalink to this headline">¶</a></h3>
+<p>Help menu entry “IDLE Help” displays a formatted html version of the
+IDLE chapter of the Library Reference.  The result, in a read-only
+tkinter text window, is close to what one sees in a web browser.
+Navigate through the text with a mousewheel,
+the scrollbar, or up and down arrow keys held down.
+Or click the TOC (Table of Contents) button and select a section
+header in the opened box.</p>
+<p>Help menu entry “Python Docs” opens the extensive sources of help,
+including tutorials, available at docs.python.org/x.y, where ‘x.y’
+is the currently running Python version.  If your system
+has an off-line copy of the docs (this may be an installation option),
+that will be opened instead.</p>
+<p>Selected URLs can be added or removed from the help menu at any time using the
+General tab of the Configure IDLE dialog .</p>
 </div>
 <div class="section" id="setting-preferences">
-<h3>26.5.4.2. Setting preferences<a class="headerlink" href="#setting-preferences" title="Permalink to this headline">¶</a></h3>
+<h3>Setting preferences<a class="headerlink" href="#setting-preferences" title="Permalink to this headline">¶</a></h3>
 <p>The font preferences, highlighting, keys, and general preferences can be
 changed via Configure IDLE on the Option menu.  Keys can be user defined;
 IDLE ships with four built-in key sets. In addition, a user can create a
 custom key set in the Configure IDLE dialog under the keys tab.</p>
 </div>
+<div class="section" id="idle-on-macos">
+<h3>IDLE on macOS<a class="headerlink" href="#idle-on-macos" title="Permalink to this headline">¶</a></h3>
+<p>Under System Preferences: Dock, one can set “Prefer tabs when opening
+documents” to “Always”.  This setting is not compatible with the tk/tkinter
+GUI framework used by IDLE, and it breaks a few IDLE features.</p>
+</div>
 <div class="section" id="extensions">
-<h3>26.5.4.3. Extensions<a class="headerlink" href="#extensions" title="Permalink to this headline">¶</a></h3>
+<h3>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 the Extensions tab of the preferences dialog. See the
 beginning of config-extensions.def in the idlelib directory for further
@@ -668,42 +779,46 @@ also used for testing.</p>
       </div>
       <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
         <div class="sphinxsidebarwrapper">
-  <h3><a href="../contents.html">Table Of Contents</a></h3>
+  <h3><a href="../contents.html">Table of Contents</a></h3>
   <ul>
-<li><a class="reference internal" href="#">26.5. IDLE</a><ul>
-<li><a class="reference internal" href="#menus">26.5.1. Menus</a><ul>
-<li><a class="reference internal" href="#file-menu-shell-and-editor">26.5.1.1. File menu (Shell and Editor)</a></li>
-<li><a class="reference internal" href="#edit-menu-shell-and-editor">26.5.1.2. Edit menu (Shell and Editor)</a></li>
-<li><a class="reference internal" href="#format-menu-editor-window-only">26.5.1.3. Format menu (Editor window only)</a></li>
-<li><a class="reference internal" href="#run-menu-editor-window-only">26.5.1.4. Run menu (Editor window only)</a></li>
-<li><a class="reference internal" href="#shell-menu-shell-window-only">26.5.1.5. Shell menu (Shell window only)</a></li>
-<li><a class="reference internal" href="#debug-menu-shell-window-only">26.5.1.6. Debug menu (Shell window only)</a></li>
-<li><a class="reference internal" href="#options-menu-shell-and-editor">26.5.1.7. Options menu (Shell and Editor)</a></li>
-<li><a class="reference internal" href="#window-menu-shell-and-editor">26.5.1.8. Window menu (Shell and Editor)</a></li>
-<li><a class="reference internal" href="#help-menu-shell-and-editor">26.5.1.9. Help menu (Shell and Editor)</a></li>
-<li><a class="reference internal" href="#context-menus">26.5.1.10. Context Menus</a></li>
+<li><a class="reference internal" href="#">IDLE</a><ul>
+<li><a class="reference internal" href="#menus">Menus</a><ul>
+<li><a class="reference internal" href="#file-menu-shell-and-editor">File menu (Shell and Editor)</a></li>
+<li><a class="reference internal" href="#edit-menu-shell-and-editor">Edit menu (Shell and Editor)</a></li>
+<li><a class="reference internal" href="#format-menu-editor-window-only">Format menu (Editor window only)</a></li>
+<li><a class="reference internal" href="#run-menu-editor-window-only">Run menu (Editor window only)</a></li>
+<li><a class="reference internal" href="#shell-menu-shell-window-only">Shell menu (Shell window only)</a></li>
+<li><a class="reference internal" href="#debug-menu-shell-window-only">Debug menu (Shell window only)</a></li>
+<li><a class="reference internal" href="#options-menu-shell-and-editor">Options menu (Shell and Editor)</a></li>
+<li><a class="reference internal" href="#window-menu-shell-and-editor">Window menu (Shell and Editor)</a></li>
+<li><a class="reference internal" href="#help-menu-shell-and-editor">Help menu (Shell and Editor)</a></li>
+<li><a class="reference internal" href="#context-menus">Context Menus</a></li>
 </ul>
 </li>
-<li><a class="reference internal" href="#editing-and-navigation">26.5.2. Editing and navigation</a><ul>
-<li><a class="reference internal" href="#automatic-indentation">26.5.2.1. Automatic indentation</a></li>
-<li><a class="reference internal" href="#completions">26.5.2.2. Completions</a></li>
-<li><a class="reference internal" href="#calltips">26.5.2.3. Calltips</a></li>
-<li><a class="reference internal" href="#python-shell-window">26.5.2.4. Python Shell window</a></li>
-<li><a class="reference internal" href="#text-colors">26.5.2.5. Text colors</a></li>
+<li><a class="reference internal" href="#editing-and-navigation">Editing and navigation</a><ul>
+<li><a class="reference internal" href="#editor-windows">Editor windows</a></li>
+<li><a class="reference internal" href="#key-bindings">Key bindings</a></li>
+<li><a class="reference internal" href="#automatic-indentation">Automatic indentation</a></li>
+<li><a class="reference internal" href="#completions">Completions</a></li>
+<li><a class="reference internal" href="#calltips">Calltips</a></li>
+<li><a class="reference internal" href="#python-shell-window">Python Shell window</a></li>
+<li><a class="reference internal" href="#text-colors">Text colors</a></li>
 </ul>
 </li>
-<li><a class="reference internal" href="#startup-and-code-execution">26.5.3. Startup and code execution</a><ul>
-<li><a class="reference internal" href="#command-line-usage">26.5.3.1. Command line usage</a></li>
-<li><a class="reference internal" href="#startup-failure">26.5.3.2. Startup failure</a></li>
-<li><a class="reference internal" href="#idle-console-differences">26.5.3.3. IDLE-console differences</a></li>
-<li><a class="reference internal" href="#developing-tkinter-applications">26.5.3.4. Developing tkinter applications</a></li>
-<li><a class="reference internal" href="#running-without-a-subprocess">26.5.3.5. Running without a subprocess</a></li>
+<li><a class="reference internal" href="#startup-and-code-execution">Startup and code execution</a><ul>
+<li><a class="reference internal" href="#command-line-usage">Command line usage</a></li>
+<li><a class="reference internal" href="#startup-failure">Startup failure</a></li>
+<li><a class="reference internal" href="#running-user-code">Running user code</a></li>
+<li><a class="reference internal" href="#user-output-in-shell">User output in Shell</a></li>
+<li><a class="reference internal" href="#developing-tkinter-applications">Developing tkinter applications</a></li>
+<li><a class="reference internal" href="#running-without-a-subprocess">Running without a subprocess</a></li>
 </ul>
 </li>
-<li><a class="reference internal" href="#help-and-preferences">26.5.4. Help and preferences</a><ul>
-<li><a class="reference internal" href="#additional-help-sources">26.5.4.1. Additional help sources</a></li>
-<li><a class="reference internal" href="#setting-preferences">26.5.4.2. Setting preferences</a></li>
-<li><a class="reference internal" href="#extensions">26.5.4.3. Extensions</a></li>
+<li><a class="reference internal" href="#help-and-preferences">Help and preferences</a><ul>
+<li><a class="reference internal" href="#help-sources">Help sources</a></li>
+<li><a class="reference internal" href="#setting-preferences">Setting preferences</a></li>
+<li><a class="reference internal" href="#idle-on-macos">IDLE on macOS</a></li>
+<li><a class="reference internal" href="#extensions">Extensions</a></li>
 </ul>
 </li>
 </ul>
@@ -712,10 +827,10 @@ also used for testing.</p>
 
   <h4>Previous topic</h4>
   <p class="topless"><a href="tkinter.scrolledtext.html"
-                        title="previous chapter">26.4. <code class="docutils literal notranslate"><span class="pre">tkinter.scrolledtext</span></code> — Scrolled Text Widget</a></p>
+                        title="previous chapter"><code class="docutils literal notranslate"><span class="pre">tkinter.scrolledtext</span></code> — Scrolled Text Widget</a></p>
   <h4>Next topic</h4>
   <p class="topless"><a href="othergui.html"
-                        title="next chapter">26.6. Other Graphical User Interface Packages</a></p>
+                        title="next chapter">Other Graphical User Interface Packages</a></p>
   <div role="note" aria-label="source link">
     <h3>This Page</h3>
     <ul class="this-page-menu">
@@ -741,10 +856,10 @@ also used for testing.</p>
           <a href="../py-modindex.html" title="Python Module Index"
              >modules</a> |</li>
         <li class="right" >
-          <a href="othergui.html" title="26.6. Other Graphical User Interface Packages"
+          <a href="othergui.html" title="Other Graphical User Interface Packages"
              >next</a> |</li>
         <li class="right" >
-          <a href="tkinter.scrolledtext.html" title="26.4. tkinter.scrolledtext — Scrolled Text Widget"
+          <a href="tkinter.scrolledtext.html" title="tkinter.scrolledtext — Scrolled Text Widget"
              >previous</a> |</li>
 
     <li><img src="../_static/py.png" alt=""
@@ -757,7 +872,7 @@ also used for testing.</p>
     </li>
 
           <li class="nav-item nav-item-1"><a href="index.html" >The Python Standard Library</a> &#187;</li>
-          <li class="nav-item nav-item-2"><a href="tk.html" >26. Graphical User Interfaces with Tk</a> &#187;</li>
+          <li class="nav-item nav-item-2"><a href="tk.html" >Graphical User Interfaces with Tk</a> &#187;</li>
     <li class="right">
 
 
@@ -784,11 +899,11 @@ also used for testing.</p>
 <br />
     <br />
 
-    Last updated on Jun 10, 2018.
+    Last updated on Nov 12, 2018.
     <a href="https://docs.python.org/3/bugs.html">Found a bug</a>?
     <br />
 
-    Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.7.4.
+    Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.8.1.
     </div>
 
   </body>
index 21b5ea5..0603ede 100644 (file)
@@ -126,7 +126,10 @@ class HelpParser(HTMLParser):
         if tag in ['h1', 'h2', 'h3']:
             self.indent(0)  # clear tag, reset indent
             if self.show:
-                self.toc.append((self.header, self.text.index('insert')))
+                indent = ('        ' if tag == 'h3' else
+                          '    ' if tag == 'h2' else
+                          '')
+                self.toc.append((indent+self.header, self.text.index('insert')))
         elif tag in ['span', 'em']:
             self.chartags = ''
         elif tag == 'a':
@@ -142,11 +145,15 @@ class HelpParser(HTMLParser):
         if self.show and not self.hdrlink:
             d = data if self.pre else data.replace('\n', ' ')
             if self.tags == 'h1':
-                self.hprefix = d[0:d.index(' ')]
-            if self.tags in ['h1', 'h2', 'h3'] and self.hprefix != '':
-                if d[0:len(self.hprefix)] == self.hprefix:
-                    d = d[len(self.hprefix):].strip()
-                self.header += d
+                try:
+                    self.hprefix = d[0:d.index(' ')]
+                except ValueError:
+                    self.hprefix = ''
+            if self.tags in ['h1', 'h2', 'h3']:
+                if (self.hprefix != '' and
+                    d[0:len(self.hprefix)] == self.hprefix):
+                    d = d[len(self.hprefix):]
+                self.header += d.strip()
             self.text.insert('end', d, (self.tags, self.chartags))
 
 
@@ -233,28 +240,28 @@ class HelpWindow(Toplevel):
 def copy_strip():
     """Copy idle.html to idlelib/help.html, stripping trailing whitespace.
 
-    Files with trailing whitespace cannot be pushed to the hg cpython
+    Files with trailing whitespace cannot be pushed to the git cpython
     repository.  For 3.x (on Windows), help.html is generated, after
-    editing idle.rst in the earliest maintenance version, with
+    editing idle.rst on the master branch, with
       sphinx-build -bhtml . build/html
       python_d.exe -c "from idlelib.help import copy_strip; copy_strip()"
-    After refreshing TortoiseHG workshop to generate a diff,
-    check  both the diff and displayed text.  Push the diff along with
-    the idle.rst change and merge both into default (or an intermediate
-    maintenance version).
-
-    When the 'earlist' version gets its final maintenance release,
-    do an update as described above, without editing idle.rst, to
-    rebase help.html on the next version of idle.rst.  Do not worry
-    about version changes as version is not displayed.  Examine other
-    changes and the result of Help -> IDLE Help.
-
-    If maintenance and default versions of idle.rst diverge, and
-    merging does not go smoothly, then consider generating
-    separate help.html files from separate idle.htmls.
+    Check build/html/library/idle.html, the help.html diff, and the text
+    displayed by Help => IDLE Help.  Add a blurb and create a PR.
+
+    It can be worthwhile to occasionally generate help.html without
+    touching idle.rst.  Changes to the master version and to the doc
+    build system may result in changes that should not changed
+    the displayed text, but might break HelpParser.
+
+    As long as master and maintenance versions of idle.rst remain the
+    same, help.html can be backported.  The internal Python version
+    number is not displayed.  If maintenance idle.rst diverges from
+    the master version, then instead of backporting help.html from
+    master, repeat the proceedure above to generate a maintenance
+    version.
     """
     src = join(abspath(dirname(dirname(dirname(__file__)))),
-               'Doc', 'build', 'html', 'library', 'idle.html')
+            'Doc', 'build', 'html', 'library', 'idle.html')
     dst = join(abspath(dirname(__file__)), 'help.html')
     with open(src, 'rb') as inn,\
          open(dst, 'wb') as out:
index 7581fe2..7e7e0ae 100644 (file)
@@ -224,7 +224,7 @@ class HyperParser:
         given index, which is empty if there is no real one.
         """
         if not self.is_in_code():
-            raise ValueError("get_expression should only be called"
+            raise ValueError("get_expression should only be called "
                              "if index is inside a code.")
 
         rawtext = self.rawtext
index 8c1c24d..583e607 100644 (file)
@@ -117,7 +117,7 @@ ConfigDialog_spec = {
            "font face of the text in the area below it.\nIn the "
            "'Highlighting' tab, try different color schemes. Clicking "
            "items in the sample program should update the choices above it."
-           "\nIn the 'Keys', 'General' and 'Extensions' tabs, test settings"
+           "\nIn the 'Keys', 'General' and 'Extensions' tabs, test settings "
            "of interest."
            "\n[Ok] to close the dialog.[Apply] to apply the settings and "
            "and [Cancel] to revert all changes.\nRe-run the test to ensure "
@@ -152,7 +152,7 @@ GetKeysDialog_spec = {
     'msg': "Test for different key modifier sequences.\n"
            "<nothing> is invalid.\n"
            "No modifier key is invalid.\n"
-           "Shift key with [a-z],[0-9], function key, move key, tab, space"
+           "Shift key with [a-z],[0-9], function key, move key, tab, space "
            "is invalid.\nNo validity checking if advanced key binding "
            "entry is used."
     }
@@ -234,7 +234,7 @@ _percolator_spec = {
     'file': 'percolator',
     'kwds': {},
     'msg': "There are two tracers which can be toggled using a checkbox.\n"
-           "Toggling a tracer 'on' by checking it should print tracer"
+           "Toggling a tracer 'on' by checking it should print tracer "
            "output to the console or to the IDLE shell.\n"
            "If both the tracers are 'on', the output from the tracer which "
            "was switched 'on' later, should be printed first\n"
@@ -335,7 +335,7 @@ ViewWindow_spec = {
 _widget_redirector_spec = {
     'file': 'redirector',
     'kwds': {},
-    'msg': "Every text insert should be printed to the console."
+    'msg': "Every text insert should be printed to the console "
            "or the IDLE shell."
     }
 
index 6e35129..a54f51f 100644 (file)
@@ -260,7 +260,7 @@ class Text:
         elif op == '!=':
             return line1 != line2 or  char1 != char2
         else:
-            raise TclError('''bad comparison operator "%s":'''
+            raise TclError('''bad comparison operator "%s": '''
                                   '''must be <, <=, ==, >=, >, or !=''' % op)
 
     # The following Text methods normally do something and return None.
index 905dc1f..dfbab6d 100644 (file)
@@ -11,7 +11,6 @@ import os.path
 import pyclbr
 from tkinter import Tk
 
-from idlelib import filelist
 from idlelib.tree import TreeNode
 
 
index 8c91972..169d054 100644 (file)
@@ -4,7 +4,6 @@
 Much of IdleConf is also exercised by ConfigDialog and test_configdialog.
 """
 from idlelib import config
-import copy
 import sys
 import os
 import tempfile
index 0847166..abe5749 100644 (file)
@@ -2,11 +2,10 @@
 
 from idlelib import config_key
 from test.support import requires
-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
+from idlelib.idle_test.mock_tk import Mbox_func
 
 
 class ValidationTest(unittest.TestCase):
index dbfcd01..5472a04 100644 (file)
@@ -8,7 +8,7 @@ requires('gui')
 import unittest
 from unittest import mock
 from idlelib.idle_test.mock_idle import Func
-from tkinter import Tk, Frame, StringVar, IntVar, BooleanVar, DISABLED, NORMAL
+from tkinter import Tk, StringVar, IntVar, BooleanVar, DISABLED, NORMAL
 from idlelib import config
 from idlelib.configdialog import idleConf, changes, tracers
 
index 48be65b..81eff39 100644 (file)
@@ -3,7 +3,6 @@
 from idlelib import rpc
 import unittest
 
-import marshal
 
 
 class CodePicklerTest(unittest.TestCase):
index bac86ac..aa5bdfb 100644 (file)
@@ -4,7 +4,7 @@
 from idlelib import zoomheight
 import unittest
 from test.support import requires
-from tkinter import Tk, Text
+from tkinter import Tk
 from idlelib.editor import EditorWindow
 
 
index fcd8dcc..f5bced5 100644 (file)
@@ -41,7 +41,7 @@ else:
             # these problems, falling back to ASCII
             locale_encoding = locale.nl_langinfo(locale.CODESET)
             if locale_encoding is None or locale_encoding == '':
-                # situation occurs on Mac OS X
+                # situation occurs on macOS
                 locale_encoding = 'ascii'
             codecs.lookup(locale_encoding)
         except (NameError, AttributeError, LookupError):
@@ -51,7 +51,7 @@ else:
             try:
                 locale_encoding = locale.getdefaultlocale()[1]
                 if locale_encoding is None or locale_encoding == '':
-                    # situation occurs on Mac OS X
+                    # situation occurs on macOS
                     locale_encoding = 'ascii'
                 codecs.lookup(locale_encoding)
             except (ValueError, LookupError):
index d3ae224..9be4ed2 100644 (file)
@@ -1,6 +1,8 @@
 """
-A number of functions that enhance IDLE on Mac OSX.
+A number of functions that enhance IDLE on macOS.
 """
+from os.path import expanduser
+import plistlib
 from sys import platform  # Used in _init_tk_type, changed by test.
 
 import tkinter
@@ -79,14 +81,47 @@ def tkVersionWarning(root):
         patchlevel = root.tk.call('info', 'patchlevel')
         if patchlevel not in ('8.5.7', '8.5.9'):
             return False
-        return (r"WARNING: The version of Tcl/Tk ({0}) in use may"
-                r" be unstable.\n"
-                r"Visit http://www.python.org/download/mac/tcltk/"
-                r" for current information.".format(patchlevel))
+        return ("WARNING: The version of Tcl/Tk ({0}) in use may"
+                " be unstable.\n"
+                "Visit http://www.python.org/download/mac/tcltk/"
+                " for current information.".format(patchlevel))
     else:
         return False
 
 
+def readSystemPreferences():
+    """
+    Fetch the macOS system preferences.
+    """
+    if platform != 'darwin':
+        return None
+
+    plist_path = expanduser('~/Library/Preferences/.GlobalPreferences.plist')
+    try:
+        with open(plist_path, 'rb') as plist_file:
+            return plistlib.load(plist_file)
+    except OSError:
+        return None
+
+
+def preferTabsPreferenceWarning():
+    """
+    Warn if "Prefer tabs when opening documents" is set to "Always".
+    """
+    if platform != 'darwin':
+        return None
+
+    prefs = readSystemPreferences()
+    if prefs and prefs.get('AppleWindowTabbingMode') == 'always':
+        return (
+            'WARNING: The system preference "Prefer tabs when opening'
+            ' documents" is set to "Always". This will cause various problems'
+            ' with IDLE. For the best experience, change this setting when'
+            ' running IDLE (via System Preferences -> Dock).'
+        )
+    return None
+
+
 ## Fix the menu and related functions.
 
 def addOpenEventSupport(root, flist):
@@ -192,7 +227,7 @@ def overrideRootMenu(root, flist):
         root.bind('<<close-all-windows>>', flist.close_all_callback)
 
         # The binding above doesn't reliably work on all versions of Tk
-        # on MacOSX. Adding command definition below does seem to do the
+        # on macOS. Adding command definition below does seem to do the
         # right thing for now.
         root.createcommand('exit', flist.close_all_callback)
 
index 4af9f1a..e962142 100644 (file)
@@ -109,7 +109,7 @@ class OutputWindow(EditorWindow):
         Return:
             Length of text inserted.
         """
-        if isinstance(s, (bytes, bytes)):
+        if isinstance(s, bytes):
             s = s.decode(iomenu.encoding, "replace")
         self.text.insert(mark, s, tags)
         self.text.see(mark)
index 1eeb915..81e7f53 100644 (file)
@@ -11,7 +11,6 @@ _closere - line that must be followed by dedent.
 _chew_ordinaryre - non-special characters.
 """
 import re
-import sys
 
 # Reason last statement is continued (or C_NONE if it's not).
 (C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE,
index 5458c59..81a97ef 100755 (executable)
@@ -12,11 +12,11 @@ except ImportError:
 # Valid arguments for the ...Awareness call below are defined in the following.
 # https://msdn.microsoft.com/en-us/library/windows/desktop/dn280512(v=vs.85).aspx
 if sys.platform == 'win32':
-    import ctypes
-    PROCESS_SYSTEM_DPI_AWARE = 1
     try:
+        import ctypes
+        PROCESS_SYSTEM_DPI_AWARE = 1
         ctypes.OleDLL('shcore').SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE)
-    except (AttributeError, OSError):
+    except (ImportError, AttributeError, OSError):
         pass
 
 import tkinter.messagebox as tkMessageBox
@@ -38,6 +38,7 @@ from platform import python_version
 import re
 import socket
 import subprocess
+from textwrap import TextWrapper
 import threading
 import time
 import tokenize
@@ -1273,6 +1274,14 @@ class PyShell(OutputWindow):
         self.set_line_and_column()
         self.io.reset_undo()
 
+    def show_warning(self, msg):
+        width = self.interp.tkconsole.width
+        wrapper = TextWrapper(width=width, tabsize=8, expand_tabs=True)
+        wrapped_msg = '\n'.join(wrapper.wrap(msg))
+        if not wrapped_msg.endswith('\n'):
+            wrapped_msg += '\n'
+        self.per.bottom.insert("iomark linestart", wrapped_msg, "stderr")
+
     def resetoutput(self):
         source = self.text.get("iomark", "end-1c")
         if self.history:
@@ -1541,12 +1550,20 @@ def main():
             shell.interp.execfile(script)
     elif shell:
         # If there is a shell window and no cmd or script in progress,
-        # check for problematic OS X Tk versions and print a warning
-        # message in the IDLE shell window; this is less intrusive
-        # than always opening a separate window.
+        # check for problematic issues and print warning message(s) in
+        # the IDLE shell window; this is less intrusive than always
+        # opening a separate window.
+
+        # Warn if using a problematic OS X Tk version.
         tkversionwarning = macosx.tkVersionWarning(root)
         if tkversionwarning:
-            shell.interp.runcommand("print('%s')" % tkversionwarning)
+            shell.show_warning(tkversionwarning)
+
+        # Warn if the "Prefer tabs when opening documents" system
+        # preference is set to "Always".
+        prefer_tabs_preference_warning = macosx.preferTabsPreferenceWarning()
+        if prefer_tabs_preference_warning:
+            shell.show_warning(prefer_tabs_preference_warning)
 
     while flist.inversedict:  # keep IDLE running while files are open.
         root.mainloop()
index e451413..dd237f7 100644 (file)
@@ -1277,7 +1277,7 @@ if HAVE_SSL:
                                  "exclusive")
             if keyfile is not None or certfile is not None:
                 import warnings
-                warnings.warn("keyfile and certfile are deprecated, use a"
+                warnings.warn("keyfile and certfile are deprecated, use a "
                               "custom ssl_context instead", DeprecationWarning, 2)
             self.keyfile = keyfile
             self.certfile = certfile
index dbdd5bf..4b2d3de 100644 (file)
@@ -66,7 +66,7 @@ class MetaPathFinder(Finder):
 
         """
         warnings.warn("MetaPathFinder.find_module() is deprecated since Python "
-                      "3.4 in favor of MetaPathFinder.find_spec()"
+                      "3.4 in favor of MetaPathFinder.find_spec() "
                       "(available since 3.4)",
                       DeprecationWarning,
                       stacklevel=2)
index b5d583c..da4a424 100644 (file)
@@ -1070,8 +1070,10 @@ def getargspec(func):
     Alternatively, use getfullargspec() for an API with a similar namedtuple
     based interface, but full support for annotations and keyword-only
     parameters.
+
+    Deprecated since Python 3.5, use `inspect.getfullargspec()`.
     """
-    warnings.warn("inspect.getargspec() is deprecated, "
+    warnings.warn("inspect.getargspec() is deprecated since Python 3.0, "
                   "use inspect.signature() or inspect.getfullargspec()",
                   DeprecationWarning, stacklevel=2)
     args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, ann = \
@@ -2802,19 +2804,25 @@ class Signature:
 
     @classmethod
     def from_function(cls, func):
-        """Constructs Signature for the given python function."""
+        """Constructs Signature for the given python function.
+
+        Deprecated since Python 3.5, use `Signature.from_callable()`.
+        """
 
-        warnings.warn("inspect.Signature.from_function() is deprecated, "
-                      "use Signature.from_callable()",
+        warnings.warn("inspect.Signature.from_function() is deprecated since "
+                      "Python 3.5, use Signature.from_callable()",
                       DeprecationWarning, stacklevel=2)
         return _signature_from_function(cls, func)
 
     @classmethod
     def from_builtin(cls, func):
-        """Constructs Signature for the given builtin function."""
+        """Constructs Signature for the given builtin function.
+
+        Deprecated since Python 3.5, use `Signature.from_callable()`.
+        """
 
-        warnings.warn("inspect.Signature.from_builtin() is deprecated, "
-                      "use Signature.from_callable()",
+        warnings.warn("inspect.Signature.from_builtin() is deprecated since "
+                      "Python 3.5, use Signature.from_callable()",
                       DeprecationWarning, stacklevel=2)
         return _signature_from_builtin(cls, func)
 
index 09cb6f6..b6c786f 100644 (file)
@@ -31,7 +31,8 @@ class FixExecfile(fixer_base.BaseFix):
         # call.
         execfile_paren = node.children[-1].children[-1].clone()
         # Construct open().read().
-        open_args = ArgList([filename.clone()], rparen=execfile_paren)
+        open_args = ArgList([filename.clone(), Comma(), String('"rb"', ' ')],
+                            rparen=execfile_paren)
         open_call = Node(syms.power, [Name("open"), open_args])
         read = [Node(syms.trailer, [Dot(), Name('read')]),
                 Node(syms.trailer, [LParen(), RParen()])]
index 8cecf3c..3da5dd8 100644 (file)
@@ -1201,36 +1201,36 @@ class Test_execfile(FixerTestCase):
 
     def test_conversion(self):
         b = """execfile("fn")"""
-        a = """exec(compile(open("fn").read(), "fn", 'exec'))"""
+        a = """exec(compile(open("fn", "rb").read(), "fn", 'exec'))"""
         self.check(b, a)
 
         b = """execfile("fn", glob)"""
-        a = """exec(compile(open("fn").read(), "fn", 'exec'), glob)"""
+        a = """exec(compile(open("fn", "rb").read(), "fn", 'exec'), glob)"""
         self.check(b, a)
 
         b = """execfile("fn", glob, loc)"""
-        a = """exec(compile(open("fn").read(), "fn", 'exec'), glob, loc)"""
+        a = """exec(compile(open("fn", "rb").read(), "fn", 'exec'), glob, loc)"""
         self.check(b, a)
 
         b = """execfile("fn", globals=glob)"""
-        a = """exec(compile(open("fn").read(), "fn", 'exec'), globals=glob)"""
+        a = """exec(compile(open("fn", "rb").read(), "fn", 'exec'), globals=glob)"""
         self.check(b, a)
 
         b = """execfile("fn", locals=loc)"""
-        a = """exec(compile(open("fn").read(), "fn", 'exec'), locals=loc)"""
+        a = """exec(compile(open("fn", "rb").read(), "fn", 'exec'), locals=loc)"""
         self.check(b, a)
 
         b = """execfile("fn", globals=glob, locals=loc)"""
-        a = """exec(compile(open("fn").read(), "fn", 'exec'), globals=glob, locals=loc)"""
+        a = """exec(compile(open("fn", "rb").read(), "fn", 'exec'), globals=glob, locals=loc)"""
         self.check(b, a)
 
     def test_spacing(self):
         b = """execfile( "fn" )"""
-        a = """exec(compile(open( "fn" ).read(), "fn", 'exec'))"""
+        a = """exec(compile(open( "fn", "rb" ).read(), "fn", 'exec'))"""
         self.check(b, a)
 
         b = """execfile("fn",  globals = glob)"""
-        a = """exec(compile(open("fn").read(), "fn", 'exec'),  globals = glob)"""
+        a = """exec(compile(open("fn", "rb").read(), "fn", 'exec'),  globals = glob)"""
         self.check(b, a)
 
 
index 3ad2cc3..2761509 100644 (file)
@@ -1033,8 +1033,8 @@ class StreamHandler(Handler):
         try:
             msg = self.format(record)
             stream = self.stream
-            stream.write(msg)
-            stream.write(self.terminator)
+            # issue 35046: merged two stream.writes into one.
+            stream.write(msg + self.terminator)
             self.flush()
         except Exception:
             self.handleError(record)
index c86dd6d..1206d8e 100644 (file)
@@ -448,6 +448,7 @@ def _default_mime_types():
         '.mht'    : 'message/rfc822',
         '.mhtml'  : 'message/rfc822',
         '.mif'    : 'application/x-mif',
+        '.mjs'    : 'application/javascript',
         '.mov'    : 'video/quicktime',
         '.movie'  : 'video/x-sgi-movie',
         '.mp2'    : 'audio/mpeg',
index 574b5db..3e9a0d6 100644 (file)
@@ -149,9 +149,8 @@ class Pool(object):
     '''
     _wrap_exception = True
 
-    @staticmethod
-    def Process(ctx, *args, **kwds):
-        return ctx.Process(*args, **kwds)
+    def Process(self, *args, **kwds):
+        return self._ctx.Process(*args, **kwds)
 
     def __init__(self, processes=None, initializer=None, initargs=(),
                  maxtasksperchild=None, context=None):
@@ -178,15 +177,13 @@ class Pool(object):
 
         self._worker_handler = threading.Thread(
             target=Pool._handle_workers,
-            args=(self._cache, self._taskqueue, self._ctx, self.Process,
-                  self._processes, self._pool, self._inqueue, self._outqueue,
-                  self._initializer, self._initargs, self._maxtasksperchild,
-                  self._wrap_exception)
+            args=(self, )
             )
         self._worker_handler.daemon = True
         self._worker_handler._state = RUN
         self._worker_handler.start()
 
+
         self._task_handler = threading.Thread(
             target=Pool._handle_tasks,
             args=(self._taskqueue, self._quick_put, self._outqueue,
@@ -212,62 +209,43 @@ class Pool(object):
             exitpriority=15
             )
 
-    @staticmethod
-    def _join_exited_workers(pool):
+    def _join_exited_workers(self):
         """Cleanup after any worker processes which have exited due to reaching
         their specified lifetime.  Returns True if any workers were cleaned up.
         """
         cleaned = False
-        for i in reversed(range(len(pool))):
-            worker = pool[i]
+        for i in reversed(range(len(self._pool))):
+            worker = self._pool[i]
             if worker.exitcode is not None:
                 # worker exited
                 util.debug('cleaning up worker %d' % i)
                 worker.join()
                 cleaned = True
-                del pool[i]
+                del self._pool[i]
         return cleaned
 
     def _repopulate_pool(self):
-        return self._repopulate_pool_static(self._ctx, self.Process,
-                                            self._processes,
-                                            self._pool, self._inqueue,
-                                            self._outqueue, self._initializer,
-                                            self._initargs,
-                                            self._maxtasksperchild,
-                                            self._wrap_exception)
-
-    @staticmethod
-    def _repopulate_pool_static(ctx, Process, processes, pool, inqueue,
-                                outqueue, initializer, initargs,
-                                maxtasksperchild, wrap_exception):
         """Bring the number of pool processes up to the specified number,
         for use after reaping workers which have exited.
         """
-        for i in range(processes - len(pool)):
-            w = Process(ctx, target=worker,
-                        args=(inqueue, outqueue,
-                              initializer,
-                              initargs, maxtasksperchild,
-                              wrap_exception)
-                       )
-            pool.append(w)
+        for i in range(self._processes - len(self._pool)):
+            w = self.Process(target=worker,
+                             args=(self._inqueue, self._outqueue,
+                                   self._initializer,
+                                   self._initargs, self._maxtasksperchild,
+                                   self._wrap_exception)
+                            )
+            self._pool.append(w)
             w.name = w.name.replace('Process', 'PoolWorker')
             w.daemon = True
             w.start()
             util.debug('added worker')
 
-    @staticmethod
-    def _maintain_pool(ctx, Process, processes, pool, inqueue, outqueue,
-                       initializer, initargs, maxtasksperchild,
-                       wrap_exception):
+    def _maintain_pool(self):
         """Clean up any exited workers and start replacements for them.
         """
-        if Pool._join_exited_workers(pool):
-            Pool._repopulate_pool_static(ctx, Process, processes, pool,
-                                         inqueue, outqueue, initializer,
-                                         initargs, maxtasksperchild,
-                                         wrap_exception)
+        if self._join_exited_workers():
+            self._repopulate_pool()
 
     def _setup_queues(self):
         self._inqueue = self._ctx.SimpleQueue()
@@ -425,20 +403,16 @@ class Pool(object):
         return result
 
     @staticmethod
-    def _handle_workers(cache, taskqueue, ctx, Process, processes, pool,
-                        inqueue, outqueue, initializer, initargs,
-                        maxtasksperchild, wrap_exception):
+    def _handle_workers(pool):
         thread = threading.current_thread()
 
         # Keep maintaining workers until the cache gets drained, unless the pool
         # is terminated.
-        while thread._state == RUN or (cache and thread._state != TERMINATE):
-            Pool._maintain_pool(ctx, Process, processes, pool, inqueue,
-                               outqueue, initializer, initargs,
-                               maxtasksperchild, wrap_exception)
+        while thread._state == RUN or (pool._cache and thread._state != TERMINATE):
+            pool._maintain_pool()
             time.sleep(0.1)
         # send sentinel to stop workers
-        taskqueue.put(None)
+        pool._taskqueue.put(None)
         util.debug('worker handler exiting')
 
     @staticmethod
@@ -820,7 +794,7 @@ class ThreadPool(Pool):
     _wrap_exception = False
 
     @staticmethod
-    def Process(ctx, *args, **kwds):
+    def Process(*args, **kwds):
         from .dummy import Process
         return Process(*args, **kwds)
 
index f0e03a2..3c820b5 100644 (file)
@@ -523,8 +523,8 @@ else:  # use native Windows method on Windows
     def abspath(path):
         """Return the absolute version of a path."""
         try:
-            return _getfullpathname(path)
-        except OSError:
+            return normpath(_getfullpathname(path))
+        except (OSError, ValueError):
             return _abspath_fallback(path)
 
 # realpath is a no-op on systems without islink support
index 4482f47..0c6fc03 100755 (executable)
@@ -243,27 +243,29 @@ def _dist_try_harder(distname, version, id):
     if os.path.exists('/var/adm/inst-log/info'):
         # SuSE Linux stores distribution information in that file
         distname = 'SuSE'
-        for line in open('/var/adm/inst-log/info'):
-            tv = line.split()
-            if len(tv) == 2:
-                tag, value = tv
-            else:
-                continue
-            if tag == 'MIN_DIST_VERSION':
-                version = value.strip()
-            elif tag == 'DIST_IDENT':
-                values = value.split('-')
-                id = values[2]
+        with open('/var/adm/inst-log/info') as f:
+            for line in f:
+                tv = line.split()
+                if len(tv) == 2:
+                    tag, value = tv
+                else:
+                    continue
+                if tag == 'MIN_DIST_VERSION':
+                    version = value.strip()
+                elif tag == 'DIST_IDENT':
+                    values = value.split('-')
+                    id = values[2]
         return distname, version, id
 
     if os.path.exists('/etc/.installed'):
         # Caldera OpenLinux has some infos in that file (thanks to Colin Kong)
-        for line in open('/etc/.installed'):
-            pkg = line.split('-')
-            if len(pkg) >= 2 and pkg[0] == 'OpenLinux':
-                # XXX does Caldera support non Intel platforms ? If yes,
-                #     where can we find the needed id ?
-                return 'OpenLinux', pkg[1], id
+        with open('/etc/.installed') as f:
+            for line in f:
+                pkg = line.split('-')
+                if len(pkg) >= 2 and pkg[0] == 'OpenLinux':
+                    # XXX does Caldera support non Intel platforms ? If yes,
+                    #     where can we find the needed id ?
+                    return 'OpenLinux', pkg[1], id
 
     if os.path.isdir('/usr/lib/setup'):
         # Check for slackware version tag file (thanks to Greg Andruk)
index d8a62c0..9796f0d 100644 (file)
@@ -436,7 +436,7 @@ if HAVE_SSL:
                                  "exclusive")
             if keyfile is not None or certfile is not None:
                 import warnings
-                warnings.warn("keyfile and certfile are deprecated, use a"
+                warnings.warn("keyfile and certfile are deprecated, use a "
                               "custom context instead", DeprecationWarning, 2)
             self.keyfile = keyfile
             self.certfile = certfile
index e92186c..ca578a5 100644 (file)
@@ -246,7 +246,12 @@ def expanduser(path):
     if i == 1:
         if 'HOME' not in os.environ:
             import pwd
-            userhome = pwd.getpwuid(os.getuid()).pw_dir
+            try:
+                userhome = pwd.getpwuid(os.getuid()).pw_dir
+            except KeyError:
+                # bpo-10496: if the current user identifier doesn't exist in the
+                # password database, return the path unchanged
+                return path
         else:
             userhome = os.environ['HOME']
     else:
@@ -257,6 +262,8 @@ def expanduser(path):
         try:
             pwent = pwd.getpwnam(name)
         except KeyError:
+            # bpo-10496: if the user name from the path doesn't exist in the
+            # password database, return the path unchanged
             return path
         userhome = pwent.pw_dir
     if isinstance(path, bytes):
index 16dc0a0..8e9dd57 100644 (file)
@@ -69,8 +69,15 @@ class PycInvalidationMode(enum.Enum):
     UNCHECKED_HASH = 3
 
 
+def _get_default_invalidation_mode():
+    if os.environ.get('SOURCE_DATE_EPOCH'):
+        return PycInvalidationMode.CHECKED_HASH
+    else:
+        return PycInvalidationMode.TIMESTAMP
+
+
 def compile(file, cfile=None, dfile=None, doraise=False, optimize=-1,
-            invalidation_mode=PycInvalidationMode.TIMESTAMP):
+            invalidation_mode=None):
     """Byte-compile one Python source file to Python bytecode.
 
     :param file: The source file name.
@@ -112,8 +119,8 @@ def compile(file, cfile=None, dfile=None, doraise=False, optimize=-1,
     the resulting file would be regular and thus not the same type of file as
     it was previously.
     """
-    if os.environ.get('SOURCE_DATE_EPOCH'):
-        invalidation_mode = PycInvalidationMode.CHECKED_HASH
+    if invalidation_mode is None:
+        invalidation_mode = _get_default_invalidation_mode()
     if cfile is None:
         if optimize >= 0:
             optimization = optimize if optimize >= 1 else ''
index 8a6b27b..09992cd 100644 (file)
@@ -958,8 +958,7 @@ class HTMLDoc(Doc):
         if name == realname:
             title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
         else:
-            if (cl and realname in cl.__dict__ and
-                cl.__dict__[realname] is object):
+            if cl and inspect.getattr_static(cl, realname, []) is object:
                 reallink = '<a href="#%s">%s</a>' % (
                     cl.__name__ + '-' + realname, realname)
                 skipdocs = 1
@@ -1375,8 +1374,7 @@ location listed above.
         if name == realname:
             title = self.bold(realname)
         else:
-            if (cl and realname in cl.__dict__ and
-                cl.__dict__[realname] is object):
+            if cl and inspect.getattr_static(cl, realname, []) is object:
                 skipdocs = 1
             title = self.bold(name) + ' = ' + realname
         argspec = None
index f5b9aa1..55fb119 100644 (file)
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Autogenerated by Sphinx on Sat Oct 20 01:59:27 2018
+# Autogenerated by Sphinx on Sun Dec 23 16:24:58 2018
 topics = {'assert': 'The "assert" statement\n'
            '**********************\n'
            '\n'
@@ -26,7 +26,8 @@ topics = {'assert': 'The "assert" statement\n'
            'implementation, the built-in variable "__debug__" is "True" under\n'
            'normal circumstances, "False" when optimization is requested '
            '(command\n'
-           'line option -O).  The current code generator emits no code for an\n'
+           'line option "-O").  The current code generator emits no code for '
+           'an\n'
            'assert statement when optimization is requested at compile time.  '
            'Note\n'
            'that it is unnecessary to include the source code for the '
@@ -87,23 +88,16 @@ topics = {'assert': 'The "assert" statement\n'
                'parentheses or square brackets, is recursively defined as '
                'follows.\n'
                '\n'
-               '* If the target list is empty: The object must also be an '
-               'empty\n'
-               '  iterable.\n'
-               '\n'
-               '* If the target list is a single target in parentheses: The '
-               'object\n'
-               '  is assigned to that target.\n'
+               '* If the target list is a single target with no trailing '
+               'comma,\n'
+               '  optionally in parentheses, the object is assigned to that '
+               'target.\n'
                '\n'
-               '* If the target list is a comma-separated list of targets, or '
-               'a\n'
-               '  single target in square brackets: The object must be an '
-               'iterable\n'
-               '  with the same number of items as there are targets in the '
-               'target\n'
-               '  list, and the items are assigned, from left to right, to '
-               'the\n'
-               '  corresponding targets.\n'
+               '* Else: The object must be an iterable with the same number of '
+               'items\n'
+               '  as there are targets in the target list, and the items are '
+               'assigned,\n'
+               '  from left to right, to the corresponding targets.\n'
                '\n'
                '  * If the target list contains one target prefixed with an\n'
                '    asterisk, called a “starred” target: The object must be '
@@ -400,9 +394,21 @@ topics = {'assert': 'The "assert" statement\n'
                'the last\n'
                '"__setitem__()" or "__setattr__()" call.\n'
                '\n'
-               'See also: **PEP 526** - Variable and attribute annotation '
-               'syntax\n'
-               '  **PEP 484** - Type hints\n',
+               'See also:\n'
+               '\n'
+               '  **PEP 526** - Syntax for Variable Annotations\n'
+               '     The proposal that added syntax for annotating the types '
+               'of\n'
+               '     variables (including class variables and instance '
+               'variables),\n'
+               '     instead of expressing them through comments.\n'
+               '\n'
+               '  **PEP 484** - Type hints\n'
+               '     The proposal that added the "typing" module to provide a '
+               'standard\n'
+               '     syntax for type annotations that can be used in static '
+               'analysis\n'
+               '     tools and IDEs.\n',
  'async': 'Coroutines\n'
           '**********\n'
           '\n'
@@ -418,18 +424,19 @@ topics = {'assert': 'The "assert" statement\n'
           '\n'
           'Execution of Python coroutines can be suspended and resumed at '
           'many\n'
-          'points (see *coroutine*).  In the body of a coroutine, any "await" '
-          'and\n'
-          '"async" identifiers become reserved keywords; "await" expressions,\n'
-          '"async for" and "async with" can only be used in coroutine bodies.\n'
+          'points (see *coroutine*).  Inside the body of a coroutine '
+          'function,\n'
+          '"await" and "async" identifiers become reserved keywords; "await"\n'
+          'expressions, "async for" and "async with" can only be used in\n'
+          'coroutine function bodies.\n'
           '\n'
           'Functions defined with "async def" syntax are always coroutine\n'
           'functions, even if they do not contain "await" or "async" '
           'keywords.\n'
           '\n'
-          'It is a "SyntaxError" to use "yield from" expressions in "async '
-          'def"\n'
-          'coroutines.\n'
+          'It is a "SyntaxError" to use a "yield from" expression inside the '
+          'body\n'
+          'of a coroutine function.\n'
           '\n'
           'An example of a coroutine function:\n'
           '\n'
@@ -475,8 +482,9 @@ topics = {'assert': 'The "assert" statement\n'
           '\n'
           'See also "__aiter__()" and "__anext__()" for details.\n'
           '\n'
-          'It is a "SyntaxError" to use "async for" statement outside of an\n'
-          '"async def" function.\n'
+          'It is a "SyntaxError" to use an "async for" statement outside the '
+          'body\n'
+          'of a coroutine function.\n'
           '\n'
           '\n'
           'The "async with" statement\n'
@@ -510,10 +518,15 @@ topics = {'assert': 'The "assert" statement\n'
           '\n'
           'See also "__aenter__()" and "__aexit__()" for details.\n'
           '\n'
-          'It is a "SyntaxError" to use "async with" statement outside of an\n'
-          '"async def" function.\n'
+          'It is a "SyntaxError" to use an "async with" statement outside the\n'
+          'body of a coroutine function.\n'
           '\n'
-          'See also: **PEP 492** - Coroutines with async and await syntax\n'
+          'See also:\n'
+          '\n'
+          '  **PEP 492** - Coroutines with async and await syntax\n'
+          '     The proposal that made coroutines a proper standalone concept '
+          'in\n'
+          '     Python, and added supporting syntax.\n'
           '\n'
           '-[ Footnotes ]-\n'
           '\n'
@@ -521,15 +534,11 @@ topics = {'assert': 'The "assert" statement\n'
           '    there is a "finally" clause which happens to raise another\n'
           '    exception. That new exception causes the old one to be lost.\n'
           '\n'
-          '[2] Currently, control “flows off the end” except in the case of\n'
-          '    an exception or the execution of a "return", "continue", or\n'
-          '    "break" statement.\n'
-          '\n'
-          '[3] A string literal appearing as the first statement in the\n'
+          '[2] A string literal appearing as the first statement in the\n'
           '    function body is transformed into the function’s "__doc__"\n'
           '    attribute and therefore the function’s *docstring*.\n'
           '\n'
-          '[4] A string literal appearing as the first statement in the class\n'
+          '[3] A string literal appearing as the first statement in the class\n'
           '    body is transformed into the namespace’s "__doc__" item and\n'
           '    therefore the class’s *docstring*.\n',
  'atom-identifiers': 'Identifiers (Names)\n'
@@ -748,7 +757,7 @@ topics = {'assert': 'The "assert" statement\n'
                      '\n'
                      '       def __setattr__(self, attr, value):\n'
                      "           print(f'Setting {attr}...')\n"
-                     '           setattr(self, attr, value)\n'
+                     '           super().__setattr__(attr, value)\n'
                      '\n'
                      '   sys.modules[__name__].__class__ = VerboseModule\n'
                      '\n'
@@ -1350,7 +1359,7 @@ topics = {'assert': 'The "assert" statement\n'
              'is\n'
              'returned.\n'
              '\n'
-             '(Note that neither "and" nor "or" restrict the value and type '
+             'Note that neither "and" nor "or" restrict the value and type '
              'they\n'
              'return to "False" and "True", but rather return the last '
              'evaluated\n'
@@ -1616,7 +1625,7 @@ topics = {'assert': 'The "assert" statement\n'
           'original global namespace. (Usually, the suite contains mostly\n'
           'function definitions.)  When the class’s suite finishes execution, '
           'its\n'
-          'execution frame is discarded but its local namespace is saved. [4] '
+          'execution frame is discarded but its local namespace is saved. [3] '
           'A\n'
           'class object is then created using the inheritance list for the '
           'base\n'
@@ -1666,8 +1675,18 @@ topics = {'assert': 'The "assert" statement\n'
           'unexpected results.  Descriptors can be used to create instance\n'
           'variables with different implementation details.\n'
           '\n'
-          'See also: **PEP 3115** - Metaclasses in Python 3 **PEP 3129** -\n'
-          '  Class Decorators\n',
+          'See also:\n'
+          '\n'
+          '  **PEP 3115** - Metaclasses in Python 3000\n'
+          '     The proposal that changed the declaration of metaclasses to '
+          'the\n'
+          '     current syntax, and the semantics for how classes with\n'
+          '     metaclasses are constructed.\n'
+          '\n'
+          '  **PEP 3129** - Class Decorators\n'
+          '     The proposal that added class decorators.  Function and '
+          'method\n'
+          '     decorators were introduced in **PEP 318**.\n',
  'comparisons': 'Comparisons\n'
                 '***********\n'
                 '\n'
@@ -2371,9 +2390,11 @@ topics = {'assert': 'The "assert" statement\n'
              'returning\n'
              'from a function that handled an exception.\n'
              '\n'
-             'The optional "else" clause is executed if and when control flows '
-             'off\n'
-             'the end of the "try" clause. [2] Exceptions in the "else" clause '
+             'The optional "else" clause is executed if the control flow '
+             'leaves the\n'
+             '"try" suite, no exception was raised, and no "return", '
+             '"continue", or\n'
+             '"break" statement was executed.  Exceptions in the "else" clause '
              'are\n'
              'not handled by the preceding "except" clauses.\n'
              '\n'
@@ -2561,7 +2582,7 @@ topics = {'assert': 'The "assert" statement\n'
              '\n'
              'The function definition does not execute the function body; this '
              'gets\n'
-             'executed only when the function is called. [3]\n'
+             'executed only when the function is called. [2]\n'
              '\n'
              'A function definition may be wrapped by one or more *decorator*\n'
              'expressions. Decorator expressions are evaluated when the '
@@ -2754,7 +2775,7 @@ topics = {'assert': 'The "assert" statement\n'
              'function definitions.)  When the class’s suite finishes '
              'execution, its\n'
              'execution frame is discarded but its local namespace is saved. '
-             '[4] A\n'
+             '[3] A\n'
              'class object is then created using the inheritance list for the '
              'base\n'
              'classes and the saved local namespace for the attribute '
@@ -2806,8 +2827,18 @@ topics = {'assert': 'The "assert" statement\n'
              'unexpected results.  Descriptors can be used to create instance\n'
              'variables with different implementation details.\n'
              '\n'
-             'See also: **PEP 3115** - Metaclasses in Python 3 **PEP 3129** -\n'
-             '  Class Decorators\n'
+             'See also:\n'
+             '\n'
+             '  **PEP 3115** - Metaclasses in Python 3000\n'
+             '     The proposal that changed the declaration of metaclasses to '
+             'the\n'
+             '     current syntax, and the semantics for how classes with\n'
+             '     metaclasses are constructed.\n'
+             '\n'
+             '  **PEP 3129** - Class Decorators\n'
+             '     The proposal that added class decorators.  Function and '
+             'method\n'
+             '     decorators were introduced in **PEP 318**.\n'
              '\n'
              '\n'
              'Coroutines\n'
@@ -2825,20 +2856,20 @@ topics = {'assert': 'The "assert" statement\n'
              '\n'
              'Execution of Python coroutines can be suspended and resumed at '
              'many\n'
-             'points (see *coroutine*).  In the body of a coroutine, any '
-             '"await" and\n'
-             '"async" identifiers become reserved keywords; "await" '
-             'expressions,\n'
-             '"async for" and "async with" can only be used in coroutine '
-             'bodies.\n'
+             'points (see *coroutine*).  Inside the body of a coroutine '
+             'function,\n'
+             '"await" and "async" identifiers become reserved keywords; '
+             '"await"\n'
+             'expressions, "async for" and "async with" can only be used in\n'
+             'coroutine function bodies.\n'
              '\n'
              'Functions defined with "async def" syntax are always coroutine\n'
              'functions, even if they do not contain "await" or "async" '
              'keywords.\n'
              '\n'
-             'It is a "SyntaxError" to use "yield from" expressions in "async '
-             'def"\n'
-             'coroutines.\n'
+             'It is a "SyntaxError" to use a "yield from" expression inside '
+             'the body\n'
+             'of a coroutine function.\n'
              '\n'
              'An example of a coroutine function:\n'
              '\n'
@@ -2884,9 +2915,9 @@ topics = {'assert': 'The "assert" statement\n'
              '\n'
              'See also "__aiter__()" and "__anext__()" for details.\n'
              '\n'
-             'It is a "SyntaxError" to use "async for" statement outside of '
-             'an\n'
-             '"async def" function.\n'
+             'It is a "SyntaxError" to use an "async for" statement outside '
+             'the body\n'
+             'of a coroutine function.\n'
              '\n'
              '\n'
              'The "async with" statement\n'
@@ -2920,11 +2951,16 @@ topics = {'assert': 'The "assert" statement\n'
              '\n'
              'See also "__aenter__()" and "__aexit__()" for details.\n'
              '\n'
-             'It is a "SyntaxError" to use "async with" statement outside of '
-             'an\n'
-             '"async def" function.\n'
+             'It is a "SyntaxError" to use an "async with" statement outside '
+             'the\n'
+             'body of a coroutine function.\n'
+             '\n'
+             'See also:\n'
              '\n'
-             'See also: **PEP 492** - Coroutines with async and await syntax\n'
+             '  **PEP 492** - Coroutines with async and await syntax\n'
+             '     The proposal that made coroutines a proper standalone '
+             'concept in\n'
+             '     Python, and added supporting syntax.\n'
              '\n'
              '-[ Footnotes ]-\n'
              '\n'
@@ -2933,16 +2969,11 @@ topics = {'assert': 'The "assert" statement\n'
              '    exception. That new exception causes the old one to be '
              'lost.\n'
              '\n'
-             '[2] Currently, control “flows off the end” except in the case '
-             'of\n'
-             '    an exception or the execution of a "return", "continue", or\n'
-             '    "break" statement.\n'
-             '\n'
-             '[3] A string literal appearing as the first statement in the\n'
+             '[2] A string literal appearing as the first statement in the\n'
              '    function body is transformed into the function’s "__doc__"\n'
              '    attribute and therefore the function’s *docstring*.\n'
              '\n'
-             '[4] A string literal appearing as the first statement in the '
+             '[3] A string literal appearing as the first statement in the '
              'class\n'
              '    body is transformed into the namespace’s "__doc__" item and\n'
              '    therefore the class’s *docstring*.\n',
@@ -3470,12 +3501,12 @@ topics = {'assert': 'The "assert" statement\n'
                   '     See '
                   'http://www.ocert.org/advisories/ocert-2011-003.html for\n'
                   '     details.Changing hash values affects the iteration '
-                  'order of\n'
-                  '     dicts, sets and other mappings.  Python has never made '
-                  'guarantees\n'
-                  '     about this ordering (and it typically varies between '
-                  '32-bit and\n'
-                  '     64-bit builds).See also "PYTHONHASHSEED".\n'
+                  'order of sets.\n'
+                  '     Python has never made guarantees about this ordering '
+                  '(and it\n'
+                  '     typically varies between 32-bit and 64-bit builds).See '
+                  'also\n'
+                  '     "PYTHONHASHSEED".\n'
                   '\n'
                   '   Changed in version 3.3: Hash randomization is enabled by '
                   'default.\n'
@@ -4378,7 +4409,7 @@ topics = {'assert': 'The "assert" statement\n'
               'argument to the interpreter) is a code block.  A script command '
               '(a\n'
               'command specified on the interpreter command line with the '
-              '‘**-c**’\n'
+              '"-c"\n'
               'option) is a code block.  The string argument passed to the '
               'built-in\n'
               'functions "eval()" and "exec()" is a code block.\n'
@@ -5394,7 +5425,7 @@ topics = {'assert': 'The "assert" statement\n'
                   '\n'
                   'The new format syntax also supports new and different '
                   'options, shown\n'
-                  'in the follow examples.\n'
+                  'in the following examples.\n'
                   '\n'
                   'Accessing arguments by position:\n'
                   '\n'
@@ -5574,7 +5605,7 @@ topics = {'assert': 'The "assert" statement\n'
              '\n'
              'The function definition does not execute the function body; this '
              'gets\n'
-             'executed only when the function is called. [3]\n'
+             'executed only when the function is called. [2]\n'
              '\n'
              'A function definition may be wrapped by one or more *decorator*\n'
              'expressions. Decorator expressions are evaluated when the '
@@ -6350,8 +6381,9 @@ topics = {'assert': 'The "assert" statement\n'
  'lambda': 'Lambdas\n'
            '*******\n'
            '\n'
-           '   lambda_expr        ::= "lambda" [parameter_list]: expression\n'
-           '   lambda_expr_nocond ::= "lambda" [parameter_list]: '
+           '   lambda_expr        ::= "lambda" [parameter_list] ":" '
+           'expression\n'
+           '   lambda_expr_nocond ::= "lambda" [parameter_list] ":" '
            'expression_nocond\n'
            '\n'
            'Lambda expressions (sometimes called lambda forms) are used to '
@@ -7453,12 +7485,6 @@ topics = {'assert': 'The "assert" statement\n'
                    'of the\n'
                    '     sequence.\n'
                    '\n'
-                   'object.__missing__(self, key)\n'
-                   '\n'
-                   '   Called by "dict"."__getitem__()" to implement '
-                   '"self[key]" for dict\n'
-                   '   subclasses when key is not in the dictionary.\n'
-                   '\n'
                    'object.__setitem__(self, key, value)\n'
                    '\n'
                    '   Called to implement assignment to "self[key]".  Same '
@@ -7486,6 +7512,12 @@ topics = {'assert': 'The "assert" statement\n'
                    '   raised for improper *key* values as for the '
                    '"__getitem__()" method.\n'
                    '\n'
+                   'object.__missing__(self, key)\n'
+                   '\n'
+                   '   Called by "dict"."__getitem__()" to implement '
+                   '"self[key]" for dict\n'
+                   '   subclasses when key is not in the dictionary.\n'
+                   '\n'
                    'object.__iter__(self)\n'
                    '\n'
                    '   This method is called when an iterator is required for '
@@ -8163,12 +8195,12 @@ topics = {'assert': 'The "assert" statement\n'
                  '     See http://www.ocert.org/advisories/ocert-2011-003.html '
                  'for\n'
                  '     details.Changing hash values affects the iteration '
-                 'order of\n'
-                 '     dicts, sets and other mappings.  Python has never made '
-                 'guarantees\n'
-                 '     about this ordering (and it typically varies between '
-                 '32-bit and\n'
-                 '     64-bit builds).See also "PYTHONHASHSEED".\n'
+                 'order of sets.\n'
+                 '     Python has never made guarantees about this ordering '
+                 '(and it\n'
+                 '     typically varies between 32-bit and 64-bit builds).See '
+                 'also\n'
+                 '     "PYTHONHASHSEED".\n'
                  '\n'
                  '   Changed in version 3.3: Hash randomization is enabled by '
                  'default.\n'
@@ -8332,7 +8364,7 @@ topics = {'assert': 'The "assert" statement\n'
                  '\n'
                  '       def __setattr__(self, attr, value):\n'
                  "           print(f'Setting {attr}...')\n"
-                 '           setattr(self, attr, value)\n'
+                 '           super().__setattr__(attr, value)\n'
                  '\n'
                  '   sys.modules[__name__].__class__ = VerboseModule\n'
                  '\n'
@@ -8918,8 +8950,8 @@ topics = {'assert': 'The "assert" statement\n'
                  '     Describes the implicit "__class__" closure reference\n'
                  '\n'
                  '\n'
-                 'Metaclass example\n'
-                 '-----------------\n'
+                 'Uses for metaclasses\n'
+                 '--------------------\n'
                  '\n'
                  'The potential uses for metaclasses are boundless. Some ideas '
                  'that have\n'
@@ -8929,48 +8961,6 @@ topics = {'assert': 'The "assert" statement\n'
                  'frameworks, and\n'
                  'automatic resource locking/synchronization.\n'
                  '\n'
-                 'Here is an example of a metaclass that uses an\n'
-                 '"collections.OrderedDict" to remember the order that class '
-                 'variables\n'
-                 'are defined:\n'
-                 '\n'
-                 '   class OrderedClass(type):\n'
-                 '\n'
-                 '       @classmethod\n'
-                 '       def __prepare__(metacls, name, bases, **kwds):\n'
-                 '           return collections.OrderedDict()\n'
-                 '\n'
-                 '       def __new__(cls, name, bases, namespace, **kwds):\n'
-                 '           result = type.__new__(cls, name, bases, '
-                 'dict(namespace))\n'
-                 '           result.members = tuple(namespace)\n'
-                 '           return result\n'
-                 '\n'
-                 '   class A(metaclass=OrderedClass):\n'
-                 '       def one(self): pass\n'
-                 '       def two(self): pass\n'
-                 '       def three(self): pass\n'
-                 '       def four(self): pass\n'
-                 '\n'
-                 '   >>> A.members\n'
-                 "   ('__module__', 'one', 'two', 'three', 'four')\n"
-                 '\n'
-                 'When the class definition for *A* gets executed, the process '
-                 'begins\n'
-                 'with calling the metaclass’s "__prepare__()" method which '
-                 'returns an\n'
-                 'empty "collections.OrderedDict".  That mapping records the '
-                 'methods and\n'
-                 'attributes of *A* as they are defined within the body of the '
-                 'class\n'
-                 'statement. Once those definitions are executed, the ordered '
-                 'dictionary\n'
-                 'is fully populated and the metaclass’s "__new__()" method '
-                 'gets\n'
-                 'invoked.  That method builds the new type and it saves the '
-                 'ordered\n'
-                 'dictionary keys in an attribute called "members".\n'
-                 '\n'
                  '\n'
                  'Customizing instance and subclass checks\n'
                  '========================================\n'
@@ -9196,12 +9186,6 @@ topics = {'assert': 'The "assert" statement\n'
                  'the\n'
                  '     sequence.\n'
                  '\n'
-                 'object.__missing__(self, key)\n'
-                 '\n'
-                 '   Called by "dict"."__getitem__()" to implement "self[key]" '
-                 'for dict\n'
-                 '   subclasses when key is not in the dictionary.\n'
-                 '\n'
                  'object.__setitem__(self, key, value)\n'
                  '\n'
                  '   Called to implement assignment to "self[key]".  Same note '
@@ -9229,6 +9213,12 @@ topics = {'assert': 'The "assert" statement\n'
                  '   raised for improper *key* values as for the '
                  '"__getitem__()" method.\n'
                  '\n'
+                 'object.__missing__(self, key)\n'
+                 '\n'
+                 '   Called by "dict"."__getitem__()" to implement "self[key]" '
+                 'for dict\n'
+                 '   subclasses when key is not in the dictionary.\n'
+                 '\n'
                  'object.__iter__(self)\n'
                  '\n'
                  '   This method is called when an iterator is required for a '
@@ -10878,8 +10868,11 @@ topics = {'assert': 'The "assert" statement\n'
         'restored to their previous values (before the call) when returning\n'
         'from a function that handled an exception.\n'
         '\n'
-        'The optional "else" clause is executed if and when control flows off\n'
-        'the end of the "try" clause. [2] Exceptions in the "else" clause are\n'
+        'The optional "else" clause is executed if the control flow leaves '
+        'the\n'
+        '"try" suite, no exception was raised, and no "return", "continue", '
+        'or\n'
+        '"break" statement was executed.  Exceptions in the "else" clause are\n'
         'not handled by the preceding "except" clauses.\n'
         '\n'
         'If "finally" is present, it specifies a ‘cleanup’ handler.  The '
@@ -12267,8 +12260,8 @@ topics = {'assert': 'The "assert" statement\n'
                  '\n'
                  '   Changed in version 3.7: Dictionary order is guaranteed to '
                  'be\n'
-                 '   insertion order.  This behavior was implementation detail '
-                 'of\n'
+                 '   insertion order.  This behavior was an implementation '
+                 'detail of\n'
                  '   CPython from 3.6.\n'
                  '\n'
                  'See also: "types.MappingProxyType" can be used to create a '
index 3c02776..f32c66b 100644 (file)
@@ -171,11 +171,15 @@ else:
         pass
 
 def copystat(src, dst, *, follow_symlinks=True):
-    """Copy all stat info (mode bits, atime, mtime, flags) from src to dst.
+    """Copy file metadata
 
-    If the optional flag `follow_symlinks` is not set, symlinks aren't followed if and
-    only if both `src` and `dst` are symlinks.
+    Copy the permission bits, last access time, last modification time, and
+    flags from `src` to `dst`. On Linux, copystat() also copies the "extended
+    attributes" where possible. The file contents, owner, and group are
+    unaffected. `src` and `dst` are path names given as strings.
 
+    If the optional flag `follow_symlinks` is not set, symlinks aren't
+    followed if and only if both `src` and `dst` are symlinks.
     """
     def _nop(*args, ns=None, follow_symlinks=None):
         pass
@@ -243,8 +247,10 @@ def copy(src, dst, *, follow_symlinks=True):
     return dst
 
 def copy2(src, dst, *, follow_symlinks=True):
-    """Copy data and all stat info ("cp -p src dst"). Return the file's
-    destination."
+    """Copy data and metadata. Return the file's destination.
+
+    Metadata is copied with copystat(). Please see the copystat function
+    for more information.
 
     The destination may be a directory.
 
index 5e1bc0b..6091c7f 100755 (executable)
@@ -762,7 +762,7 @@ class SMTP:
                                  "exclusive")
             if keyfile is not None or certfile is not None:
                 import warnings
-                warnings.warn("keyfile and certfile are deprecated, use a"
+                warnings.warn("keyfile and certfile are deprecated, use a "
                               "custom context instead", DeprecationWarning, 2)
             if context is None:
                 context = ssl._create_stdlib_context(certfile=certfile,
@@ -1019,7 +1019,7 @@ if _have_ssl:
                                  "exclusive")
             if keyfile is not None or certfile is not None:
                 import warnings
-                warnings.warn("keyfile and certfile are deprecated, use a"
+                warnings.warn("keyfile and certfile are deprecated, use a "
                               "custom context instead", DeprecationWarning, 2)
             self.keyfile = keyfile
             self.certfile = certfile
index 9dfd21b..f037791 100644 (file)
@@ -230,6 +230,9 @@ class BaseServer:
 
                 while not self.__shutdown_request:
                     ready = selector.select(poll_interval)
+                    # bpo-35017: shutdown() called during select(), exit immediately.
+                    if self.__shutdown_request:
+                        break
                     if ready:
                         self._handle_request_noblock()
 
index 34cd233..1c59a3c 100644 (file)
@@ -256,24 +256,6 @@ class RegressionTests(unittest.TestCase):
         cur.execute("pragma page_size")
         row = cur.fetchone()
 
-    def CheckSetDict(self):
-        """
-        See http://bugs.python.org/issue7478
-
-        It was possible to successfully register callbacks that could not be
-        hashed. Return codes of PyDict_SetItem were not checked properly.
-        """
-        class NotHashable:
-            def __call__(self, *args, **kw):
-                pass
-            def __hash__(self):
-                raise TypeError()
-        var = NotHashable()
-        self.assertRaises(TypeError, self.con.create_function, var)
-        self.assertRaises(TypeError, self.con.create_aggregate, var)
-        self.assertRaises(TypeError, self.con.set_authorizer, var)
-        self.assertRaises(TypeError, self.con.set_progress_handler, var)
-
     def CheckConnectionCall(self):
         """
         Call a connection with a non-string SQL request: check error handling
@@ -398,9 +380,72 @@ class RegressionTests(unittest.TestCase):
         support.gc_collect()
 
 
+class UnhashableFunc:
+    __hash__ = None
+
+    def __init__(self, return_value=None):
+        self.calls = 0
+        self.return_value = return_value
+
+    def __call__(self, *args, **kwargs):
+        self.calls += 1
+        return self.return_value
+
+
+class UnhashableCallbacksTestCase(unittest.TestCase):
+    """
+    https://bugs.python.org/issue34052
+
+    Registering unhashable callbacks raises TypeError, callbacks are not
+    registered in SQLite after such registration attempt.
+    """
+    def setUp(self):
+        self.con = sqlite.connect(':memory:')
+
+    def tearDown(self):
+        self.con.close()
+
+    def test_progress_handler(self):
+        f = UnhashableFunc(return_value=0)
+        with self.assertRaisesRegex(TypeError, 'unhashable type'):
+            self.con.set_progress_handler(f, 1)
+        self.con.execute('SELECT 1')
+        self.assertFalse(f.calls)
+
+    def test_func(self):
+        func_name = 'func_name'
+        f = UnhashableFunc()
+        with self.assertRaisesRegex(TypeError, 'unhashable type'):
+            self.con.create_function(func_name, 0, f)
+        msg = 'no such function: %s' % func_name
+        with self.assertRaisesRegex(sqlite.OperationalError, msg):
+            self.con.execute('SELECT %s()' % func_name)
+        self.assertFalse(f.calls)
+
+    def test_authorizer(self):
+        f = UnhashableFunc(return_value=sqlite.SQLITE_DENY)
+        with self.assertRaisesRegex(TypeError, 'unhashable type'):
+            self.con.set_authorizer(f)
+        self.con.execute('SELECT 1')
+        self.assertFalse(f.calls)
+
+    def test_aggr(self):
+        class UnhashableType(type):
+            __hash__ = None
+        aggr_name = 'aggr_name'
+        with self.assertRaisesRegex(TypeError, 'unhashable type'):
+            self.con.create_aggregate(aggr_name, 0, UnhashableType('Aggr', (), {}))
+        msg = 'no such function: %s' % aggr_name
+        with self.assertRaisesRegex(sqlite.OperationalError, msg):
+            self.con.execute('SELECT %s()' % aggr_name)
+
+
 def suite():
     regression_suite = unittest.makeSuite(RegressionTests, "Check")
-    return unittest.TestSuite((regression_suite,))
+    return unittest.TestSuite((
+        regression_suite,
+        unittest.makeSuite(UnhashableCallbacksTestCase),
+    ))
 
 def test():
     runner = unittest.TextTestRunner()
index 38aa389..d1d9866 100644 (file)
@@ -884,8 +884,8 @@ class SSLSocket(socket):
             return self._sslobj.session_reused
 
     def dup(self):
-        raise NotImplemented("Can't dup() %s instances" %
-                             self.__class__.__name__)
+        raise NotImplementedError("Can't dup() %s instances" %
+                                  self.__class__.__name__)
 
     def _checkClosed(self, msg=None):
         # raise an exception here if you wish to check for spurious closes
index 00844bc..3c1abb7 100644 (file)
@@ -261,9 +261,7 @@ def _args_from_interpreter_flags():
         # 'inspect': 'i',
         # 'interactive': 'i',
         'dont_write_bytecode': 'B',
-        'no_user_site': 's',
         'no_site': 'S',
-        'ignore_environment': 'E',
         'verbose': 'v',
         'bytes_warning': 'b',
         'quiet': 'q',
@@ -275,6 +273,14 @@ def _args_from_interpreter_flags():
         if v > 0:
             args.append('-' + opt * v)
 
+    if sys.flags.isolated:
+        args.append('-I')
+    else:
+        if sys.flags.ignore_environment:
+            args.append('-E')
+        if sys.flags.no_user_site:
+            args.append('-s')
+
     # -W options
     warnopts = sys.warnoptions[:]
     bytes_warning = sys.flags.bytes_warning
index 6e416a9..abcb5d4 100644 (file)
@@ -2465,6 +2465,7 @@ class _TestPool(BaseTestCase):
             with self.Pool(2) as p:
                 r = p.map_async(sqr, L)
                 self.assertEqual(r.get(), expected)
+            p.join()
             self.assertRaises(ValueError, p.map_async, sqr, L)
 
     @classmethod
@@ -2482,6 +2483,7 @@ class _TestPool(BaseTestCase):
                     exc = e
                 else:
                     self.fail('expected RuntimeError')
+            p.join()
             self.assertIs(type(exc), RuntimeError)
             self.assertEqual(exc.args, (123,))
             cause = exc.__cause__
@@ -2506,6 +2508,7 @@ class _TestPool(BaseTestCase):
                     self.fail('expected SayWhenError')
                 self.assertIs(type(exc), SayWhenError)
                 self.assertIs(exc.__cause__, None)
+            p.join()
 
     @classmethod
     def _test_wrapped_exception(cls):
@@ -2516,6 +2519,7 @@ class _TestPool(BaseTestCase):
         with self.Pool(1) as p:
             with self.assertRaises(RuntimeError):
                 p.apply(self._test_wrapped_exception)
+        p.join()
 
     def test_map_no_failfast(self):
         # Issue #23992: the fail-fast behaviour when an exception is raised
@@ -2551,12 +2555,6 @@ class _TestPool(BaseTestCase):
         # they were released too.
         self.assertEqual(CountedObject.n_instances, 0)
 
-    def test_del_pool(self):
-        p = self.Pool(1)
-        wr = weakref.ref(p)
-        del p
-        gc.collect()
-        self.assertIsNone(wr())
 
 def raising():
     raise KeyError("key")
index 9c6e71c..2f838c4 100644 (file)
@@ -13,6 +13,7 @@ import sys
 import os
 import pickle
 import random
+import re
 import struct
 import unittest
 
@@ -37,6 +38,7 @@ import _testcapi
 import _strptime
 #
 
+pickle_loads = {pickle.loads, pickle._loads}
 
 pickle_choices = [(pickle, pickle, proto)
                   for proto in range(pickle.HIGHEST_PROTOCOL + 1)]
@@ -302,6 +304,8 @@ class TestTimeZone(unittest.TestCase):
         self.assertEqual('UTC+09:30', timezone(9.5 * HOUR).tzname(None))
         self.assertEqual('UTC-00:01', timezone(timedelta(minutes=-1)).tzname(None))
         self.assertEqual('XYZ', timezone(-5 * HOUR, 'XYZ').tzname(None))
+        # bpo-34482: Check that surrogates are handled properly.
+        self.assertEqual('\ud800', timezone(ZERO, '\ud800').tzname(None))
 
         # Sub-minute offsets:
         self.assertEqual('UTC+01:06:40', timezone(timedelta(0, 4000)).tzname(None))
@@ -893,19 +897,50 @@ class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
         class BadInt(int):
             def __mul__(self, other):
                 return Prod()
+            def __rmul__(self, other):
+                return Prod()
+            def __floordiv__(self, other):
+                return Prod()
+            def __rfloordiv__(self, other):
+                return Prod()
 
         class Prod:
+            def __add__(self, other):
+                return Sum()
             def __radd__(self, other):
                 return Sum()
 
         class Sum(int):
             def __divmod__(self, other):
-                # negative remainder
-                return (0, -1)
-
-        timedelta(microseconds=BadInt(1))
-        timedelta(hours=BadInt(1))
-        timedelta(weeks=BadInt(1))
+                return divmodresult
+
+        for divmodresult in [None, (), (0, 1, 2), (0, -1)]:
+            with self.subTest(divmodresult=divmodresult):
+                # The following examples should not crash.
+                try:
+                    timedelta(microseconds=BadInt(1))
+                except TypeError:
+                    pass
+                try:
+                    timedelta(hours=BadInt(1))
+                except TypeError:
+                    pass
+                try:
+                    timedelta(weeks=BadInt(1))
+                except (TypeError, ValueError):
+                    pass
+                try:
+                    timedelta(1) * BadInt(1)
+                except (TypeError, ValueError):
+                    pass
+                try:
+                    BadInt(1) * timedelta(1)
+                except TypeError:
+                    pass
+                try:
+                    timedelta(1) // BadInt(1)
+                except TypeError:
+                    pass
 
 
 #############################################################################
@@ -1307,6 +1342,12 @@ class TestDate(HarmlessMixedComparison, unittest.TestCase):
             except ValueError:
                 pass
 
+        # bpo-34482: Check that surrogates don't cause a crash.
+        try:
+            t.strftime('%y\ud800%m')
+        except UnicodeEncodeError:
+            pass
+
         #check that this standard extension works
         t.strftime("%f")
 
@@ -1394,6 +1435,19 @@ class TestDate(HarmlessMixedComparison, unittest.TestCase):
             self.assertEqual(orig, derived)
         self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2))
 
+    def test_compat_unpickle(self):
+        tests = [
+            b"cdatetime\ndate\n(S'\\x07\\xdf\\x0b\\x1b'\ntR.",
+            b'cdatetime\ndate\n(U\x04\x07\xdf\x0b\x1btR.',
+            b'\x80\x02cdatetime\ndate\nU\x04\x07\xdf\x0b\x1b\x85R.',
+        ]
+        args = 2015, 11, 27
+        expected = self.theclass(*args)
+        for data in tests:
+            for loads in pickle_loads:
+                derived = loads(data, encoding='latin1')
+                self.assertEqual(derived, expected)
+
     def test_compare(self):
         t1 = self.theclass(2, 3, 4)
         t2 = self.theclass(2, 3, 4)
@@ -1746,6 +1800,9 @@ class TestDateTime(TestDate):
         self.assertEqual(t.isoformat('T'), "0001-02-03T04:05:01.000123")
         self.assertEqual(t.isoformat(' '), "0001-02-03 04:05:01.000123")
         self.assertEqual(t.isoformat('\x00'), "0001-02-03\x0004:05:01.000123")
+        # bpo-34482: Check that surrogates are handled properly.
+        self.assertEqual(t.isoformat('\ud800'),
+                         "0001-02-03\ud80004:05:01.000123")
         self.assertEqual(t.isoformat(timespec='hours'), "0001-02-03T04")
         self.assertEqual(t.isoformat(timespec='minutes'), "0001-02-03T04:05")
         self.assertEqual(t.isoformat(timespec='seconds'), "0001-02-03T04:05:01")
@@ -1754,6 +1811,8 @@ class TestDateTime(TestDate):
         self.assertEqual(t.isoformat(timespec='auto'), "0001-02-03T04:05:01.000123")
         self.assertEqual(t.isoformat(sep=' ', timespec='minutes'), "0001-02-03 04:05")
         self.assertRaises(ValueError, t.isoformat, timespec='foo')
+        # bpo-34482: Check that surrogates are handled properly.
+        self.assertRaises(ValueError, t.isoformat, timespec='\ud800')
         # str is ISO format with the separator forced to a blank.
         self.assertEqual(str(t), "0001-02-03 04:05:01.000123")
 
@@ -2053,6 +2112,24 @@ class TestDateTime(TestDate):
             derived = unpickler.loads(green)
             self.assertEqual(orig, derived)
 
+    def test_compat_unpickle(self):
+        tests = [
+            b'cdatetime\ndatetime\n('
+            b"S'\\x07\\xdf\\x0b\\x1b\\x14;\\x01\\x00\\x10\\x00'\ntR.",
+
+            b'cdatetime\ndatetime\n('
+            b'U\n\x07\xdf\x0b\x1b\x14;\x01\x00\x10\x00tR.',
+
+            b'\x80\x02cdatetime\ndatetime\n'
+            b'U\n\x07\xdf\x0b\x1b\x14;\x01\x00\x10\x00\x85R.',
+        ]
+        args = 2015, 11, 27, 20, 59, 1, 64**2
+        expected = self.theclass(*args)
+        for data in tests:
+            for loads in pickle_loads:
+                derived = loads(data, encoding='latin1')
+                self.assertEqual(derived, expected)
+
     def test_more_compare(self):
         # The test_compare() inherited from TestDate covers the error cases.
         # We just want to test lexicographic ordering on the members datetime
@@ -2285,6 +2362,19 @@ class TestDateTime(TestDate):
         self.assertIs(type(expected), self.theclass)
         self.assertIs(type(got), self.theclass)
 
+        # bpo-34482: Check that surrogates are handled properly.
+        inputs = [
+            ('2004-12-01\ud80013:02:47.197', '%Y-%m-%d\ud800%H:%M:%S.%f'),
+            ('2004\ud80012-01 13:02:47.197', '%Y\ud800%m-%d %H:%M:%S.%f'),
+            ('2004-12-01 13:02\ud80047.197', '%Y-%m-%d %H:%M\ud800%S.%f'),
+        ]
+        for string, format in inputs:
+            with self.subTest(string=string, format=format):
+                expected = _strptime._strptime_datetime(self.theclass, string,
+                                                        format)
+                got = self.theclass.strptime(string, format)
+                self.assertEqual(expected, got)
+
         strptime = self.theclass.strptime
         self.assertEqual(strptime("+0002", "%z").utcoffset(), 2 * MINUTE)
         self.assertEqual(strptime("-0002", "%z").utcoffset(), -2 * MINUTE)
@@ -2352,6 +2442,12 @@ class TestDateTime(TestDate):
             t = t.replace(tzinfo=tz)
             self.assertEqual(t.strftime("%z"), "-0200" + z)
 
+        # bpo-34482: Check that surrogates don't cause a crash.
+        try:
+            t.strftime('%y\ud800%m %H\ud800%M')
+        except UnicodeEncodeError:
+            pass
+
     def test_extract(self):
         dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
         self.assertEqual(dt.date(), date(2002, 3, 4))
@@ -2676,6 +2772,14 @@ class TestDateTime(TestDate):
                 with self.assertRaises(ValueError):
                     self.theclass.fromisoformat(bad_str)
 
+    def test_fromisoformat_fails_surrogate(self):
+        # Test that when fromisoformat() fails with a surrogate character as
+        # the separator, the error message contains the original string
+        dtstr = "2018-01-03\ud80001:0113"
+
+        with self.assertRaisesRegex(ValueError, re.escape(repr(dtstr))):
+            self.theclass.fromisoformat(dtstr)
+
     def test_fromisoformat_utc(self):
         dt_str = '2014-04-19T13:21:13+00:00'
         dt = self.theclass.fromisoformat(dt_str)
@@ -2869,6 +2973,8 @@ class TestTime(HarmlessMixedComparison, unittest.TestCase):
         self.assertEqual(t.isoformat(timespec='microseconds'), "12:34:56.123456")
         self.assertEqual(t.isoformat(timespec='auto'), "12:34:56.123456")
         self.assertRaises(ValueError, t.isoformat, timespec='monkey')
+        # bpo-34482: Check that surrogates are handled properly.
+        self.assertRaises(ValueError, t.isoformat, timespec='\ud800')
 
         t = self.theclass(hour=12, minute=34, second=56, microsecond=999500)
         self.assertEqual(t.isoformat(timespec='milliseconds'), "12:34:56.999")
@@ -2919,6 +3025,12 @@ class TestTime(HarmlessMixedComparison, unittest.TestCase):
         # A naive object replaces %z and %Z with empty strings.
         self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
 
+        # bpo-34482: Check that surrogates don't cause a crash.
+        try:
+            t.strftime('%H\ud800%M')
+        except UnicodeEncodeError:
+            pass
+
     def test_format(self):
         t = self.theclass(1, 2, 3, 4)
         self.assertEqual(t.__format__(''), str(t))
@@ -2989,6 +3101,19 @@ class TestTime(HarmlessMixedComparison, unittest.TestCase):
             derived = unpickler.loads(green)
             self.assertEqual(orig, derived)
 
+    def test_compat_unpickle(self):
+        tests = [
+            b"cdatetime\ntime\n(S'\\x14;\\x10\\x00\\x10\\x00'\ntR.",
+            b'cdatetime\ntime\n(U\x06\x14;\x10\x00\x10\x00tR.',
+            b'\x80\x02cdatetime\ntime\nU\x06\x14;\x10\x00\x10\x00\x85R.',
+        ]
+        args = 20, 59, 16, 64**2
+        expected = self.theclass(*args)
+        for data in tests:
+            for loads in pickle_loads:
+                derived = loads(data, encoding='latin1')
+                self.assertEqual(derived, expected)
+
     def test_bool(self):
         # time is always True.
         cls = self.theclass
@@ -3361,6 +3486,40 @@ class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
             self.assertEqual(derived.tzname(), 'cookie')
         self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2))
 
+    def test_compat_unpickle(self):
+        tests = [
+            b"cdatetime\ntime\n(S'\\x05\\x06\\x07\\x01\\xe2@'\n"
+            b"ctest.datetimetester\nPicklableFixedOffset\n(tR"
+            b"(dS'_FixedOffset__offset'\ncdatetime\ntimedelta\n"
+            b"(I-1\nI68400\nI0\ntRs"
+            b"S'_FixedOffset__dstoffset'\nNs"
+            b"S'_FixedOffset__name'\nS'cookie'\nsbtR.",
+
+            b'cdatetime\ntime\n(U\x06\x05\x06\x07\x01\xe2@'
+            b'ctest.datetimetester\nPicklableFixedOffset\n)R'
+            b'}(U\x14_FixedOffset__offsetcdatetime\ntimedelta\n'
+            b'(J\xff\xff\xff\xffJ0\x0b\x01\x00K\x00tR'
+            b'U\x17_FixedOffset__dstoffsetN'
+            b'U\x12_FixedOffset__nameU\x06cookieubtR.',
+
+            b'\x80\x02cdatetime\ntime\nU\x06\x05\x06\x07\x01\xe2@'
+            b'ctest.datetimetester\nPicklableFixedOffset\n)R'
+            b'}(U\x14_FixedOffset__offsetcdatetime\ntimedelta\n'
+            b'J\xff\xff\xff\xffJ0\x0b\x01\x00K\x00\x87R'
+            b'U\x17_FixedOffset__dstoffsetN'
+            b'U\x12_FixedOffset__nameU\x06cookieub\x86R.',
+        ]
+
+        tinfo = PicklableFixedOffset(-300, 'cookie')
+        expected = self.theclass(5, 6, 7, 123456, tzinfo=tinfo)
+        for data in tests:
+            for loads in pickle_loads:
+                derived = loads(data, encoding='latin1')
+                self.assertEqual(derived, expected, repr(data))
+                self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
+                self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
+                self.assertEqual(derived.tzname(), 'cookie')
+
     def test_more_bool(self):
         # time is always True.
         cls = self.theclass
@@ -3709,6 +3868,43 @@ class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
             self.assertEqual(derived.tzname(), 'cookie')
         self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2))
 
+    def test_compat_unpickle(self):
+        tests = [
+            b'cdatetime\ndatetime\n'
+            b"(S'\\x07\\xdf\\x0b\\x1b\\x14;\\x01\\x01\\xe2@'\n"
+            b'ctest.datetimetester\nPicklableFixedOffset\n(tR'
+            b"(dS'_FixedOffset__offset'\ncdatetime\ntimedelta\n"
+            b'(I-1\nI68400\nI0\ntRs'
+            b"S'_FixedOffset__dstoffset'\nNs"
+            b"S'_FixedOffset__name'\nS'cookie'\nsbtR.",
+
+            b'cdatetime\ndatetime\n'
+            b'(U\n\x07\xdf\x0b\x1b\x14;\x01\x01\xe2@'
+            b'ctest.datetimetester\nPicklableFixedOffset\n)R'
+            b'}(U\x14_FixedOffset__offsetcdatetime\ntimedelta\n'
+            b'(J\xff\xff\xff\xffJ0\x0b\x01\x00K\x00tR'
+            b'U\x17_FixedOffset__dstoffsetN'
+            b'U\x12_FixedOffset__nameU\x06cookieubtR.',
+
+            b'\x80\x02cdatetime\ndatetime\n'
+            b'U\n\x07\xdf\x0b\x1b\x14;\x01\x01\xe2@'
+            b'ctest.datetimetester\nPicklableFixedOffset\n)R'
+            b'}(U\x14_FixedOffset__offsetcdatetime\ntimedelta\n'
+            b'J\xff\xff\xff\xffJ0\x0b\x01\x00K\x00\x87R'
+            b'U\x17_FixedOffset__dstoffsetN'
+            b'U\x12_FixedOffset__nameU\x06cookieub\x86R.',
+        ]
+        args = 2015, 11, 27, 20, 59, 1, 123456
+        tinfo = PicklableFixedOffset(-300, 'cookie')
+        expected = self.theclass(*args, **{'tzinfo': tinfo})
+        for data in tests:
+            for loads in pickle_loads:
+                derived = loads(data, encoding='latin1')
+                self.assertEqual(derived, expected)
+                self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
+                self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
+                self.assertEqual(derived.tzname(), 'cookie')
+
     def test_extreme_hashes(self):
         # If an attempt is made to hash these via subtracting the offset
         # then hashing a datetime object, OverflowError results.  The
index 1caeafe..25c169b 100644 (file)
@@ -10,6 +10,7 @@ sub-second periodicity (contrarily to signal()).
 
 import contextlib
 import faulthandler
+import fcntl
 import os
 import select
 import signal
@@ -44,25 +45,28 @@ class EINTRBaseTest(unittest.TestCase):
     # sleep_time > signal_period
     sleep_time = 0.2
 
-    @classmethod
-    def setUpClass(cls):
-        cls.orig_handler = signal.signal(signal.SIGALRM, lambda *args: None)
-        signal.setitimer(signal.ITIMER_REAL, cls.signal_delay,
-                         cls.signal_period)
+    def sighandler(self, signum, frame):
+        self.signals += 1
 
-        # Issue #25277: Use faulthandler to try to debug a hang on FreeBSD
+    def setUp(self):
+        self.signals = 0
+        self.orig_handler = signal.signal(signal.SIGALRM, self.sighandler)
+        signal.setitimer(signal.ITIMER_REAL, self.signal_delay,
+                         self.signal_period)
+
+        # Use faulthandler as watchdog to debug when a test hangs
+        # (timeout of 10 minutes)
         if hasattr(faulthandler, 'dump_traceback_later'):
             faulthandler.dump_traceback_later(10 * 60, exit=True,
                                               file=sys.__stderr__)
 
-    @classmethod
-    def stop_alarm(cls):
+    @staticmethod
+    def stop_alarm():
         signal.setitimer(signal.ITIMER_REAL, 0, 0)
 
-    @classmethod
-    def tearDownClass(cls):
-        cls.stop_alarm()
-        signal.signal(signal.SIGALRM, cls.orig_handler)
+    def tearDown(self):
+        self.stop_alarm()
+        signal.signal(signal.SIGALRM, self.orig_handler)
         if hasattr(faulthandler, 'cancel_dump_traceback_later'):
             faulthandler.cancel_dump_traceback_later()
 
@@ -345,16 +349,18 @@ class SocketEINTRTest(EINTRBaseTest):
         fp = open(path, 'w')
         fp.close()
 
+    @unittest.skipIf(sys.platform == "darwin",
+                     "hangs under macOS; see bpo-25234, bpo-35363")
     def test_open(self):
         self._test_open("fp = open(path, 'r')\nfp.close()",
                         self.python_open)
 
-    @unittest.skipIf(sys.platform == 'darwin', "hangs under OS X; see issue #25234")
     def os_open(self, path):
         fd = os.open(path, os.O_WRONLY)
         os.close(fd)
 
-    @unittest.skipIf(sys.platform == "darwin", "hangs under OS X; see issue #25234")
+    @unittest.skipIf(sys.platform == "darwin",
+                     "hangs under macOS; see bpo-25234, bpo-35363")
     def test_os_open(self):
         self._test_open("fd = os.open(path, os.O_RDONLY)\nos.close(fd)",
                         self.os_open)
@@ -481,14 +487,43 @@ class SelectEINTRTest(EINTRBaseTest):
         self.assertGreaterEqual(dt, self.sleep_time)
 
 
-def test_main():
-    support.run_unittest(
-        OSEINTRTest,
-        SocketEINTRTest,
-        TimeEINTRTest,
-        SignalEINTRTest,
-        SelectEINTRTest)
+class FNTLEINTRTest(EINTRBaseTest):
+    def _lock(self, lock_func, lock_name):
+        self.addCleanup(support.unlink, support.TESTFN)
+        code = '\n'.join((
+            "import fcntl, time",
+            "with open('%s', 'wb') as f:" % support.TESTFN,
+            "   fcntl.%s(f, fcntl.LOCK_EX)" % lock_name,
+            "   time.sleep(%s)" % self.sleep_time))
+        start_time = time.monotonic()
+        proc = self.subprocess(code)
+        with kill_on_error(proc):
+            with open(support.TESTFN, 'wb') as f:
+                while True:  # synchronize the subprocess
+                    dt = time.monotonic() - start_time
+                    if dt > 60.0:
+                        raise Exception("failed to sync child in %.1f sec" % dt)
+                    try:
+                        lock_func(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
+                        lock_func(f, fcntl.LOCK_UN)
+                        time.sleep(0.01)
+                    except BlockingIOError:
+                        break
+                # the child locked the file just a moment ago for 'sleep_time' seconds
+                # that means that the lock below will block for 'sleep_time' minus some
+                # potential context switch delay
+                lock_func(f, fcntl.LOCK_EX)
+                dt = time.monotonic() - start_time
+                self.assertGreaterEqual(dt, self.sleep_time)
+                self.stop_alarm()
+            proc.wait()
+
+    def test_lockf(self):
+        self._lock(fcntl.lockf, "lockf")
+
+    def test_flock(self):
+        self._lock(fcntl.flock, "flock")
 
 
 if __name__ == "__main__":
-    test_main()
+    unittest.main()
index 2af839a..0927032 100644 (file)
@@ -170,7 +170,7 @@ def _create_parser():
     group.add_argument('--wait', action='store_true',
                        help='wait for user input, e.g., allow a debugger '
                             'to be attached')
-    group.add_argument('--slaveargs', metavar='ARGS')
+    group.add_argument('--worker-args', metavar='ARGS')
     group.add_argument('-S', '--start', metavar='START',
                        help='the name of the test at which to start.' +
                             more_details)
@@ -271,7 +271,8 @@ def _create_parser():
     group.add_argument('--junit-xml', dest='xmlpath', metavar='FILENAME',
                        help='writes JUnit-style XML results to the specified '
                             'file')
-
+    group.add_argument('--tempdir', dest='tempdir', metavar='PATH',
+                       help='override the working directory for the test run')
     return parser
 
 
@@ -383,8 +384,7 @@ def _parse_args(args, **kwargs):
     if ns.match_filename:
         if ns.match_tests is None:
             ns.match_tests = []
-        filename = os.path.join(support.SAVEDCWD, ns.match_filename)
-        with open(filename) as fp:
+        with open(ns.match_filename) as fp:
             for line in fp:
                 ns.match_tests.append(line.strip())
 
index b491a08..8d44caf 100644 (file)
@@ -14,7 +14,7 @@ from test.libregrtest.cmdline import _parse_args
 from test.libregrtest.runtest import (
     findtests, runtest, get_abs_module,
     STDTESTS, NOTTESTS, PASSED, FAILED, ENV_CHANGED, SKIPPED, RESOURCE_DENIED,
-    INTERRUPTED, CHILD_ERROR,
+    INTERRUPTED, CHILD_ERROR, TEST_DID_NOT_RUN,
     PROGRESS_MIN_TIME, format_test_result)
 from test.libregrtest.setup import setup_tests
 from test.libregrtest.utils import removepy, count, format_duration, printlist
@@ -79,6 +79,7 @@ class Regrtest:
         self.resource_denieds = []
         self.environment_changed = []
         self.rerun = []
+        self.run_no_tests = []
         self.first_result = None
         self.interrupted = False
 
@@ -118,6 +119,8 @@ class Regrtest:
         elif ok == RESOURCE_DENIED:
             self.skipped.append(test)
             self.resource_denieds.append(test)
+        elif ok == TEST_DID_NOT_RUN:
+            self.run_no_tests.append(test)
         elif ok != INTERRUPTED:
             raise ValueError("invalid test result: %r" % ok)
 
@@ -368,6 +371,11 @@ class Regrtest:
             print("%s:" % count(len(self.rerun), "re-run test"))
             printlist(self.rerun)
 
+        if self.run_no_tests:
+            print()
+            print(count(len(self.run_no_tests), "test"), "run no tests:")
+            printlist(self.run_no_tests)
+
     def run_tests_sequential(self):
         if self.ns.trace:
             import trace
@@ -458,6 +466,9 @@ class Regrtest:
             result.append("FAILURE")
         elif self.ns.fail_env_changed and self.environment_changed:
             result.append("ENV CHANGED")
+        elif not any((self.good, self.bad, self.skipped, self.interrupted,
+            self.environment_changed)):
+            result.append("NO TEST RUN")
 
         if self.interrupted:
             result.append("INTERRUPTED")
@@ -550,12 +561,12 @@ class Regrtest:
 
     def main(self, tests=None, **kwargs):
         global TEMPDIR
+        self.ns = self.parse_args(kwargs)
 
-        if sysconfig.is_python_build():
-            try:
-                os.mkdir(TEMPDIR)
-            except FileExistsError:
-                pass
+        if self.ns.tempdir:
+            TEMPDIR = self.ns.tempdir
+
+        os.makedirs(TEMPDIR, exist_ok=True)
 
         # Define a writable temp dir that will be used as cwd while running
         # the tests. The name of the dir includes the pid to allow parallel
@@ -571,8 +582,6 @@ class Regrtest:
             self._main(tests, kwargs)
 
     def _main(self, tests, kwargs):
-        self.ns = self.parse_args(kwargs)
-
         if self.ns.huntrleaks:
             warmup, repetitions, _ = self.ns.huntrleaks
             if warmup < 1 or repetitions < 1:
@@ -582,9 +591,9 @@ class Regrtest:
                 print(msg, file=sys.stderr, flush=True)
                 sys.exit(2)
 
-        if self.ns.slaveargs is not None:
-            from test.libregrtest.runtest_mp import run_tests_slave
-            run_tests_slave(self.ns.slaveargs)
+        if self.ns.worker_args is not None:
+            from test.libregrtest.runtest_mp import run_tests_worker
+            run_tests_worker(self.ns.worker_args)
 
         if self.ns.wait:
             input("Press any key to continue...")
index 4f41080..466b522 100644 (file)
@@ -19,6 +19,7 @@ SKIPPED = -2
 RESOURCE_DENIED = -3
 INTERRUPTED = -4
 CHILD_ERROR = -5   # error in a child process
+TEST_DID_NOT_RUN = -6   # error in a child process
 
 _FORMAT_TEST_RESULT = {
     PASSED: '%s passed',
@@ -28,6 +29,7 @@ _FORMAT_TEST_RESULT = {
     RESOURCE_DENIED: '%s skipped (resource denied)',
     INTERRUPTED: '%s interrupted',
     CHILD_ERROR: '%s crashed',
+    TEST_DID_NOT_RUN: '%s run no tests',
 }
 
 # Minimum duration of a test to display its duration or to mention that
@@ -94,6 +96,7 @@ def runtest(ns, test):
         ENV_CHANGED      test failed because it changed the execution environment
         FAILED           test failed
         PASSED           test passed
+        EMPTY_TEST_SUITE test ran no subtests.
 
     If ns.xmlpath is not None, xml_data is a list containing each
     generated testsuite element.
@@ -197,6 +200,8 @@ def runtest_inner(ns, test, display_failure=True):
             else:
                 print("test", test, "failed", file=sys.stderr, flush=True)
         return FAILED, test_time
+    except support.TestDidNotRun:
+        return TEST_DID_NOT_RUN, test_time
     except:
         msg = traceback.format_exc()
         if not ns.pgo:
index 779c429..6190574 100644 (file)
@@ -24,23 +24,23 @@ WAIT_PROGRESS = 2.0   # seconds
 
 
 def run_test_in_subprocess(testname, ns):
-    """Run the given test in a subprocess with --slaveargs.
+    """Run the given test in a subprocess with --worker-args.
 
     ns is the option Namespace parsed from command-line arguments. regrtest
-    is invoked in a subprocess with the --slaveargs argument; when the
+    is invoked in a subprocess with the --worker-args argument; when the
     subprocess exits, its return code, stdout and stderr are returned as a
     3-tuple.
     """
     from subprocess import Popen, PIPE
 
     ns_dict = vars(ns)
-    slaveargs = (ns_dict, testname)
-    slaveargs = json.dumps(slaveargs)
+    worker_args = (ns_dict, testname)
+    worker_args = json.dumps(worker_args)
 
     cmd = [sys.executable, *support.args_from_interpreter_flags(),
            '-u',    # Unbuffered stdout and stderr
            '-m', 'test.regrtest',
-           '--slaveargs', slaveargs]
+           '--worker-args', worker_args]
     if ns.pgo:
         cmd += ['--pgo']
 
@@ -58,8 +58,8 @@ def run_test_in_subprocess(testname, ns):
     return retcode, stdout, stderr
 
 
-def run_tests_slave(slaveargs):
-    ns_dict, testname = json.loads(slaveargs)
+def run_tests_worker(worker_args):
+    ns_dict, testname = json.loads(worker_args)
     ns = types.SimpleNamespace(**ns_dict)
 
     setup_tests(ns)
index 4a563e9..085c45d 100644 (file)
@@ -142,7 +142,7 @@ def collect_platform(info_add):
     info_add('platform.python_implementation',
              platform.python_implementation())
     info_add('platform.platform',
-             platform.platform(aliased=True, terse=True))
+             platform.platform(aliased=True))
 
 
 def collect_locale(info_add):
@@ -199,32 +199,74 @@ def collect_os(info_add):
     call_func(info_add, 'os.cpu_count', os, 'cpu_count')
     call_func(info_add, 'os.loadavg', os, 'getloadavg')
 
-    # Get environment variables: filter to list
-    # to not leak sensitive information
-    ENV_VARS = (
+    # Environment variables used by the stdlib and tests. Don't log the full
+    # environment: filter to list to not leak sensitive information.
+    #
+    # HTTP_PROXY is not logged because it can contain a password.
+    ENV_VARS = frozenset((
+        "APPDATA",
+        "AR",
+        "ARCHFLAGS",
+        "ARFLAGS",
+        "AUDIODEV",
         "CC",
+        "CFLAGS",
+        "COLUMNS",
+        "COMPUTERNAME",
         "COMSPEC",
+        "CPP",
+        "CPPFLAGS",
         "DISPLAY",
+        "DISTUTILS_DEBUG",
         "DISTUTILS_USE_SDK",
         "DYLD_LIBRARY_PATH",
+        "ENSUREPIP_OPTIONS",
+        "HISTORY_FILE",
         "HOME",
         "HOMEDRIVE",
         "HOMEPATH",
+        "IDLESTARTUP",
         "LANG",
+        "LDFLAGS",
+        "LDSHARED",
         "LD_LIBRARY_PATH",
+        "LINES",
         "MACOSX_DEPLOYMENT_TARGET",
+        "MAILCAPS",
         "MAKEFLAGS",
+        "MIXERDEV",
         "MSSDK",
         "PATH",
+        "PATHEXT",
+        "PIP_CONFIG_FILE",
+        "PLAT",
+        "POSIXLY_CORRECT",
+        "PY_SAX_PARSER",
+        "ProgramFiles",
+        "ProgramFiles(x86)",
+        "RUNNING_ON_VALGRIND",
         "SDK_TOOLS_BIN",
+        "SERVER_SOFTWARE",
         "SHELL",
+        "SOURCE_DATE_EPOCH",
+        "SYSTEMROOT",
         "TEMP",
         "TERM",
+        "TILE_LIBRARY",
+        "TIX_LIBRARY",
         "TMP",
         "TMPDIR",
+        "TRAVIS",
+        "TZ",
         "USERPROFILE",
+        "VIRTUAL_ENV",
         "WAYLAND_DISPLAY",
-    )
+        "WINDIR",
+        "_PYTHON_HOST_PLATFORM",
+        "_PYTHON_PROJECT_BASE",
+        "_PYTHON_SYSCONFIGDATA_NAME",
+        "__PYVENV_LAUNCHER__",
+    ))
     for name, value in os.environ.items():
         uname = name.upper()
         if (uname in ENV_VARS
@@ -365,7 +407,10 @@ def collect_sysconfig(info_add):
         'OPT',
         'PY_CFLAGS',
         'PY_CFLAGS_NODIST',
+        'PY_CORE_LDFLAGS',
         'PY_LDFLAGS',
+        'PY_LDFLAGS_NODIST',
+        'PY_STDMODULE_CFLAGS',
         'Py_DEBUG',
         'Py_ENABLE_SHARED',
         'SHELL',
@@ -525,6 +570,33 @@ def collect_cc(info_add):
     info_add('CC.version', text)
 
 
+def collect_gdbm(info_add):
+    try:
+        from _gdbm import _GDBM_VERSION
+    except ImportError:
+        return
+
+    info_add('gdbm.GDBM_VERSION', '.'.join(map(str, _GDBM_VERSION)))
+
+
+def collect_get_config(info_add):
+    # Dump global configuration variables, _PyCoreConfig
+    # and _PyMainInterpreterConfig
+    try:
+        from _testcapi import get_global_config, get_core_config, get_main_config
+    except ImportError:
+        return
+
+    for prefix, get_config_func in (
+        ('global_config', get_global_config),
+        ('core_config', get_core_config),
+        ('main_config', get_main_config),
+    ):
+        config = get_config_func()
+        for key in sorted(config):
+            info_add('%s[%s]' % (prefix, key), repr(config[key]))
+
+
 def collect_info(info):
     error = False
     info_add = info.add
@@ -552,6 +624,8 @@ def collect_info(info):
         collect_testcapi,
         collect_resource,
         collect_cc,
+        collect_gdbm,
+        collect_get_config,
 
         # Collecting from tests should be last as they have side effects.
         collect_test_socket,
index 877be51..168b5da 100644 (file)
@@ -21,25 +21,19 @@ class InterProcessSignalTests(unittest.TestCase):
         self.got_signals['SIGUSR1'] += 1
         raise SIGUSR1Exception
 
-    def wait_signal(self, child, signame, exc_class=None):
-        try:
-            if child is not None:
-                # This wait should be interrupted by exc_class
-                # (if set)
-                child.wait()
-
-            timeout = 10.0
-            deadline = time.monotonic() + timeout
-
-            while time.monotonic() < deadline:
-                if self.got_signals[signame]:
-                    return
-                signal.pause()
-        except BaseException as exc:
-            if exc_class is not None and isinstance(exc, exc_class):
-                # got the expected exception
+    def wait_signal(self, child, signame):
+        if child is not None:
+            # This wait should be interrupted by exc_class
+            # (if set)
+            child.wait()
+
+        timeout = 10.0
+        deadline = time.monotonic() + timeout
+
+        while time.monotonic() < deadline:
+            if self.got_signals[signame]:
                 return
-            raise
+            signal.pause()
 
         self.fail('signal %s not received after %s seconds'
                   % (signame, timeout))
@@ -65,8 +59,9 @@ class InterProcessSignalTests(unittest.TestCase):
         self.assertEqual(self.got_signals, {'SIGHUP': 1, 'SIGUSR1': 0,
                                             'SIGALRM': 0})
 
-        with self.subprocess_send_signal(pid, "SIGUSR1") as child:
-            self.wait_signal(child, 'SIGUSR1', SIGUSR1Exception)
+        with self.assertRaises(SIGUSR1Exception):
+            with self.subprocess_send_signal(pid, "SIGUSR1") as child:
+                self.wait_signal(child, 'SIGUSR1')
         self.assertEqual(self.got_signals, {'SIGHUP': 1, 'SIGUSR1': 1,
                                             'SIGALRM': 0})
 
@@ -75,8 +70,9 @@ class InterProcessSignalTests(unittest.TestCase):
             child.wait()
 
         try:
-            signal.alarm(1)
-            self.wait_signal(None, 'SIGALRM', KeyboardInterrupt)
+            with self.assertRaises(KeyboardInterrupt):
+                signal.alarm(1)
+                self.wait_signal(None, 'SIGALRM')
             self.assertEqual(self.got_signals, {'SIGHUP': 1, 'SIGUSR1': 1,
                                                 'SIGALRM': 0})
         finally:
index 4127b50..512e354 100644 (file)
@@ -71,7 +71,7 @@ __all__ = [
     # globals
     "PIPE_MAX_SIZE", "verbose", "max_memuse", "use_resources", "failfast",
     # exceptions
-    "Error", "TestFailed", "ResourceDenied",
+    "Error", "TestFailed", "TestDidNotRun", "ResourceDenied",
     # imports
     "import_module", "import_fresh_module", "CleanImport",
     # modules
@@ -117,6 +117,9 @@ class Error(Exception):
 class TestFailed(Error):
     """Test failed."""
 
+class TestDidNotRun(Error):
+    """Test did not run any subtests."""
+
 class ResourceDenied(unittest.SkipTest):
     """Test skipped because it requested a disallowed resource.
 
@@ -849,7 +852,11 @@ for character in (
     '\u20AC',
 ):
     try:
-        os.fsdecode(os.fsencode(character))
+        # If Python is set up to use the legacy 'mbcs' in Windows,
+        # 'replace' error mode is used, and encode() returns b'?'
+        # for characters missing in the ANSI codepage
+        if os.fsdecode(os.fsencode(character)) != character:
+            raise UnicodeError
     except UnicodeError:
         pass
     else:
@@ -1881,16 +1888,17 @@ def _filter_suite(suite, pred):
 
 def _run_suite(suite):
     """Run tests from a unittest.TestSuite-derived class."""
-    runner = get_test_runner(sys.stdout, verbosity=verbose)
-
-    # TODO: Remove this before merging (here for easy comparison with old impl)
-    #runner = unittest.TextTestRunner(sys.stdout, verbosity=2, failfast=failfast)
+    runner = get_test_runner(sys.stdout,
+                             verbosity=verbose,
+                             capture_output=(junit_xml_list is not None))
 
     result = runner.run(suite)
 
     if junit_xml_list is not None:
         junit_xml_list.append(result.get_xml_element())
 
+    if not result.testsRun:
+        raise TestDidNotRun
     if not result.wasSuccessful():
         if len(result.errors) == 1 and not result.failures:
             err = result.errors[0][1]
index 8988d3d..67e126d 100644 (file)
@@ -60,10 +60,12 @@ class RegressionTestResult(unittest.TextTestResult):
             e.set('time', f'{time.perf_counter() - self.__start_time:0.6f}')
 
         if capture:
-            stdout = self._stdout_buffer.getvalue().rstrip()
-            ET.SubElement(e, 'system-out').text = stdout
-            stderr = self._stderr_buffer.getvalue().rstrip()
-            ET.SubElement(e, 'system-err').text = stderr
+            if self._stdout_buffer is not None:
+                stdout = self._stdout_buffer.getvalue().rstrip()
+                ET.SubElement(e, 'system-out').text = stdout
+            if self._stderr_buffer is not None:
+                stderr = self._stderr_buffer.getvalue().rstrip()
+                ET.SubElement(e, 'system-err').text = stderr
 
         for k, v in args.items():
             if not k or not v:
@@ -152,23 +154,24 @@ class RegressionTestResult(unittest.TextTestResult):
         return e
 
 class QuietRegressionTestRunner:
-    def __init__(self, stream):
+    def __init__(self, stream, buffer=False):
         self.result = RegressionTestResult(stream, None, 0)
+        self.result.buffer = buffer
 
     def run(self, test):
         test(self.result)
         return self.result
 
-def get_test_runner_class(verbosity):
+def get_test_runner_class(verbosity, buffer=False):
     if verbosity:
         return functools.partial(unittest.TextTestRunner,
                                  resultclass=RegressionTestResult,
-                                 buffer=True,
+                                 buffer=buffer,
                                  verbosity=verbosity)
-    return QuietRegressionTestRunner
+    return functools.partial(QuietRegressionTestRunner, buffer=buffer)
 
-def get_test_runner(stream, verbosity):
-    return get_test_runner_class(verbosity)(stream)
+def get_test_runner(stream, verbosity, capture_output=False):
+    return get_test_runner_class(verbosity, capture_output)(stream)
 
 if __name__ == '__main__':
     class TestTests(unittest.TestCase):
index bcba8ca..388a2b1 100644 (file)
@@ -24,7 +24,7 @@ class Test_OSXSupport(unittest.TestCase):
         for cv in ('CFLAGS', 'LDFLAGS', 'CPPFLAGS',
                             'BASECFLAGS', 'BLDSHARED', 'LDSHARED', 'CC',
                             'CXX', 'PY_CFLAGS', 'PY_LDFLAGS', 'PY_CPPFLAGS',
-                            'PY_CORE_CFLAGS'):
+                            'PY_CORE_CFLAGS', 'PY_CORE_LDFLAGS'):
             if cv in self.env:
                 self.env.unset(cv)
 
diff --git a/Lib/test/test_asyncio/keycert3.pem b/Lib/test/test_asyncio/keycert3.pem
deleted file mode 100644 (file)
index 5bfa62c..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMLgD0kAKDb5cFyP
-jbwNfR5CtewdXC+kMXAWD8DLxiTTvhMW7qVnlwOm36mZlszHKvsRf05lT4pegiFM
-9z2j1OlaN+ci/X7NU22TNN6crYSiN77FjYJP464j876ndSxyD+rzys386T+1r1aZ
-aggEdkj1TsSsv1zWIYKlPIjlvhuxAgMBAAECgYA0aH+T2Vf3WOPv8KdkcJg6gCRe
-yJKXOWgWRcicx/CUzOEsTxmFIDPLxqAWA3k7v0B+3vjGw5Y9lycV/5XqXNoQI14j
-y09iNsumds13u5AKkGdTJnZhQ7UKdoVHfuP44ZdOv/rJ5/VD6F4zWywpe90pcbK+
-AWDVtusgGQBSieEl1QJBAOyVrUG5l2yoUBtd2zr/kiGm/DYyXlIthQO/A3/LngDW
-5/ydGxVsT7lAVOgCsoT+0L4efTh90PjzW8LPQrPBWVMCQQDS3h/FtYYd5lfz+FNL
-9CEe1F1w9l8P749uNUD0g317zv1tatIqVCsQWHfVHNdVvfQ+vSFw38OORO00Xqs9
-1GJrAkBkoXXEkxCZoy4PteheO/8IWWLGGr6L7di6MzFl1lIqwT6D8L9oaV2vynFT
-DnKop0pa09Unhjyw57KMNmSE2SUJAkEArloTEzpgRmCq4IK2/NpCeGdHS5uqRlbh
-1VIa/xGps7EWQl5Mn8swQDel/YP3WGHTjfx7pgSegQfkyaRtGpZ9OQJAa9Vumj8m
-JAAtI0Bnga8hgQx7BhTQY4CadDxyiRGOGYhwUzYVCqkb2sbVRH9HnwUaJT7cWBY3
-RnJdHOMXWem7/w==
------END PRIVATE KEY-----
-Certificate:
-    Data:
-        Version: 1 (0x0)
-        Serial Number: 12723342612721443281 (0xb09264b1f2da21d1)
-    Signature Algorithm: sha1WithRSAEncryption
-        Issuer: C=XY, O=Python Software Foundation CA, CN=our-ca-server
-        Validity
-            Not Before: Jan  4 19:47:07 2013 GMT
-            Not After : Nov 13 19:47:07 2022 GMT
-        Subject: C=XY, L=Castle Anthrax, O=Python Software Foundation, CN=localhost
-        Subject Public Key Info:
-            Public Key Algorithm: rsaEncryption
-                Public-Key: (1024 bit)
-                Modulus:
-                    00:c2:e0:0f:49:00:28:36:f9:70:5c:8f:8d:bc:0d:
-                    7d:1e:42:b5:ec:1d:5c:2f:a4:31:70:16:0f:c0:cb:
-                    c6:24:d3:be:13:16:ee:a5:67:97:03:a6:df:a9:99:
-                    96:cc:c7:2a:fb:11:7f:4e:65:4f:8a:5e:82:21:4c:
-                    f7:3d:a3:d4:e9:5a:37:e7:22:fd:7e:cd:53:6d:93:
-                    34:de:9c:ad:84:a2:37:be:c5:8d:82:4f:e3:ae:23:
-                    f3:be:a7:75:2c:72:0f:ea:f3:ca:cd:fc:e9:3f:b5:
-                    af:56:99:6a:08:04:76:48:f5:4e:c4:ac:bf:5c:d6:
-                    21:82:a5:3c:88:e5:be:1b:b1
-                Exponent: 65537 (0x10001)
-    Signature Algorithm: sha1WithRSAEncryption
-         2f:42:5f:a3:09:2c:fa:51:88:c7:37:7f:ea:0e:63:f0:a2:9a:
-         e5:5a:e2:c8:20:f0:3f:60:bc:c8:0f:b6:c6:76:ce:db:83:93:
-         f5:a3:33:67:01:8e:04:cd:00:9a:73:fd:f3:35:86:fa:d7:13:
-         e2:46:c6:9d:c0:29:53:d4:a9:90:b8:77:4b:e6:83:76:e4:92:
-         d6:9c:50:cf:43:d0:c6:01:77:61:9a:de:9b:70:f7:72:cd:59:
-         00:31:69:d9:b4:ca:06:9c:6d:c3:c7:80:8c:68:e6:b5:a2:f8:
-         ef:1d:bb:16:9f:77:77:ef:87:62:22:9b:4d:69:a4:3a:1a:f1:
-         21:5e:8c:32:ac:92:fd:15:6b:18:c2:7f:15:0d:98:30:ca:75:
-         8f:1a:71:df:da:1d:b2:ef:9a:e8:2d:2e:02:fd:4a:3c:aa:96:
-         0b:06:5d:35:b3:3d:24:87:4b:e0:b0:58:60:2f:45:ac:2e:48:
-         8a:b0:99:10:65:27:ff:cc:b1:d8:fd:bd:26:6b:b9:0c:05:2a:
-         f4:45:63:35:51:07:ed:83:85:fe:6f:69:cb:bb:40:a8:ae:b6:
-         3b:56:4a:2d:a4:ed:6d:11:2c:4d:ed:17:24:fd:47:bc:d3:41:
-         a2:d3:06:fe:0c:90:d8:d8:94:26:c4:ff:cc:a1:d8:42:77:eb:
-         fc:a9:94:71
------BEGIN CERTIFICATE-----
-MIICpDCCAYwCCQCwkmSx8toh0TANBgkqhkiG9w0BAQUFADBNMQswCQYDVQQGEwJY
-WTEmMCQGA1UECgwdUHl0aG9uIFNvZnR3YXJlIEZvdW5kYXRpb24gQ0ExFjAUBgNV
-BAMMDW91ci1jYS1zZXJ2ZXIwHhcNMTMwMTA0MTk0NzA3WhcNMjIxMTEzMTk0NzA3
-WjBfMQswCQYDVQQGEwJYWTEXMBUGA1UEBxMOQ2FzdGxlIEFudGhyYXgxIzAhBgNV
-BAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMRIwEAYDVQQDEwlsb2NhbGhv
-c3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMLgD0kAKDb5cFyPjbwNfR5C
-tewdXC+kMXAWD8DLxiTTvhMW7qVnlwOm36mZlszHKvsRf05lT4pegiFM9z2j1Ola
-N+ci/X7NU22TNN6crYSiN77FjYJP464j876ndSxyD+rzys386T+1r1aZaggEdkj1
-TsSsv1zWIYKlPIjlvhuxAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAC9CX6MJLPpR
-iMc3f+oOY/CimuVa4sgg8D9gvMgPtsZ2ztuDk/WjM2cBjgTNAJpz/fM1hvrXE+JG
-xp3AKVPUqZC4d0vmg3bkktacUM9D0MYBd2Ga3ptw93LNWQAxadm0ygacbcPHgIxo
-5rWi+O8duxafd3fvh2Iim01ppDoa8SFejDKskv0VaxjCfxUNmDDKdY8acd/aHbLv
-mugtLgL9SjyqlgsGXTWzPSSHS+CwWGAvRawuSIqwmRBlJ//Msdj9vSZruQwFKvRF
-YzVRB+2Dhf5vacu7QKiutjtWSi2k7W0RLE3tFyT9R7zTQaLTBv4MkNjYlCbE/8yh
-2EJ36/yplHE=
------END CERTIFICATE-----
diff --git a/Lib/test/test_asyncio/pycacert.pem b/Lib/test/test_asyncio/pycacert.pem
deleted file mode 100644 (file)
index 09b1f3e..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-Certificate:
-    Data:
-        Version: 3 (0x2)
-        Serial Number: 12723342612721443280 (0xb09264b1f2da21d0)
-    Signature Algorithm: sha1WithRSAEncryption
-        Issuer: C=XY, O=Python Software Foundation CA, CN=our-ca-server
-        Validity
-            Not Before: Jan  4 19:47:07 2013 GMT
-            Not After : Jan  2 19:47:07 2023 GMT
-        Subject: C=XY, O=Python Software Foundation CA, CN=our-ca-server
-        Subject Public Key Info:
-            Public Key Algorithm: rsaEncryption
-                Public-Key: (2048 bit)
-                Modulus:
-                    00:e7:de:e9:e3:0c:9f:00:b6:a1:fd:2b:5b:96:d2:
-                    6f:cc:e0:be:86:b9:20:5e:ec:03:7a:55:ab:ea:a4:
-                    e9:f9:49:85:d2:66:d5:ed:c7:7a:ea:56:8e:2d:8f:
-                    e7:42:e2:62:28:a9:9f:d6:1b:8e:eb:b5:b4:9c:9f:
-                    14:ab:df:e6:94:8b:76:1d:3e:6d:24:61:ed:0c:bf:
-                    00:8a:61:0c:df:5c:c8:36:73:16:00:cd:47:ba:6d:
-                    a4:a4:74:88:83:23:0a:19:fc:09:a7:3c:4a:4b:d3:
-                    e7:1d:2d:e4:ea:4c:54:21:f3:26:db:89:37:18:d4:
-                    02:bb:40:32:5f:a4:ff:2d:1c:f7:d4:bb:ec:8e:cf:
-                    5c:82:ac:e6:7c:08:6c:48:85:61:07:7f:25:e0:5c:
-                    e0:bc:34:5f:e0:b9:04:47:75:c8:47:0b:8d:bc:d6:
-                    c8:68:5f:33:83:62:d2:20:44:35:b1:ad:81:1a:8a:
-                    cd:bc:35:b0:5c:8b:47:d6:18:e9:9c:18:97:cc:01:
-                    3c:29:cc:e8:1e:e4:e4:c1:b8:de:e7:c2:11:18:87:
-                    5a:93:34:d8:a6:25:f7:14:71:eb:e4:21:a2:d2:0f:
-                    2e:2e:d4:62:00:35:d3:d6:ef:5c:60:4b:4c:a9:14:
-                    e2:dd:15:58:46:37:33:26:b7:e7:2e:5d:ed:42:e4:
-                    c5:4d
-                Exponent: 65537 (0x10001)
-        X509v3 extensions:
-            X509v3 Subject Key Identifier: 
-                BC:DD:62:D9:76:DA:1B:D2:54:6B:CF:E0:66:9B:1E:1E:7B:56:0C:0B
-            X509v3 Authority Key Identifier: 
-                keyid:BC:DD:62:D9:76:DA:1B:D2:54:6B:CF:E0:66:9B:1E:1E:7B:56:0C:0B
-
-            X509v3 Basic Constraints: 
-                CA:TRUE
-    Signature Algorithm: sha1WithRSAEncryption
-         7d:0a:f5:cb:8d:d3:5d:bd:99:8e:f8:2b:0f:ba:eb:c2:d9:a6:
-         27:4f:2e:7b:2f:0e:64:d8:1c:35:50:4e:ee:fc:90:b9:8d:6d:
-         a8:c5:c6:06:b0:af:f3:2d:bf:3b:b8:42:07:dd:18:7d:6d:95:
-         54:57:85:18:60:47:2f:eb:78:1b:f9:e8:17:fd:5a:0d:87:17:
-         28:ac:4c:6a:e6:bc:29:f4:f4:55:70:29:42:de:85:ea:ab:6c:
-         23:06:64:30:75:02:8e:53:bc:5e:01:33:37:cc:1e:cd:b8:a4:
-         fd:ca:e4:5f:65:3b:83:1c:86:f1:55:02:a0:3a:8f:db:91:b7:
-         40:14:b4:e7:8d:d2:ee:73:ba:e3:e5:34:2d:bc:94:6f:4e:24:
-         06:f7:5f:8b:0e:a7:8e:6b:de:5e:75:f4:32:9a:50:b1:44:33:
-         9a:d0:05:e2:78:82:ff:db:da:8a:63:eb:a9:dd:d1:bf:a0:61:
-         ad:e3:9e:8a:24:5d:62:0e:e7:4c:91:7f:ef:df:34:36:3b:2f:
-         5d:f5:84:b2:2f:c4:6d:93:96:1a:6f:30:28:f1:da:12:9a:64:
-         b4:40:33:1d:bd:de:2b:53:a8:ea:be:d6:bc:4e:96:f5:44:fb:
-         32:18:ae:d5:1f:f6:69:af:b6:4e:7b:1d:58:ec:3b:a9:53:a3:
-         5e:58:c8:9e
------BEGIN CERTIFICATE-----
-MIIDbTCCAlWgAwIBAgIJALCSZLHy2iHQMA0GCSqGSIb3DQEBBQUAME0xCzAJBgNV
-BAYTAlhZMSYwJAYDVQQKDB1QeXRob24gU29mdHdhcmUgRm91bmRhdGlvbiBDQTEW
-MBQGA1UEAwwNb3VyLWNhLXNlcnZlcjAeFw0xMzAxMDQxOTQ3MDdaFw0yMzAxMDIx
-OTQ3MDdaME0xCzAJBgNVBAYTAlhZMSYwJAYDVQQKDB1QeXRob24gU29mdHdhcmUg
-Rm91bmRhdGlvbiBDQTEWMBQGA1UEAwwNb3VyLWNhLXNlcnZlcjCCASIwDQYJKoZI
-hvcNAQEBBQADggEPADCCAQoCggEBAOfe6eMMnwC2of0rW5bSb8zgvoa5IF7sA3pV
-q+qk6flJhdJm1e3HeupWji2P50LiYiipn9Ybjuu1tJyfFKvf5pSLdh0+bSRh7Qy/
-AIphDN9cyDZzFgDNR7ptpKR0iIMjChn8Cac8SkvT5x0t5OpMVCHzJtuJNxjUArtA
-Ml+k/y0c99S77I7PXIKs5nwIbEiFYQd/JeBc4Lw0X+C5BEd1yEcLjbzWyGhfM4Ni
-0iBENbGtgRqKzbw1sFyLR9YY6ZwYl8wBPCnM6B7k5MG43ufCERiHWpM02KYl9xRx
-6+QhotIPLi7UYgA109bvXGBLTKkU4t0VWEY3Mya35y5d7ULkxU0CAwEAAaNQME4w
-HQYDVR0OBBYEFLzdYtl22hvSVGvP4GabHh57VgwLMB8GA1UdIwQYMBaAFLzdYtl2
-2hvSVGvP4GabHh57VgwLMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB
-AH0K9cuN0129mY74Kw+668LZpidPLnsvDmTYHDVQTu78kLmNbajFxgawr/Mtvzu4
-QgfdGH1tlVRXhRhgRy/reBv56Bf9Wg2HFyisTGrmvCn09FVwKULeheqrbCMGZDB1
-Ao5TvF4BMzfMHs24pP3K5F9lO4MchvFVAqA6j9uRt0AUtOeN0u5zuuPlNC28lG9O
-JAb3X4sOp45r3l519DKaULFEM5rQBeJ4gv/b2opj66nd0b+gYa3jnookXWIO50yR
-f+/fNDY7L131hLIvxG2TlhpvMCjx2hKaZLRAMx293itTqOq+1rxOlvVE+zIYrtUf
-9mmvtk57HVjsO6lTo15YyJ4=
------END CERTIFICATE-----
diff --git a/Lib/test/test_asyncio/ssl_cert.pem b/Lib/test/test_asyncio/ssl_cert.pem
deleted file mode 100644 (file)
index 47a7d7e..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICVDCCAb2gAwIBAgIJANfHOBkZr8JOMA0GCSqGSIb3DQEBBQUAMF8xCzAJBgNV
-BAYTAlhZMRcwFQYDVQQHEw5DYXN0bGUgQW50aHJheDEjMCEGA1UEChMaUHl0aG9u
-IFNvZnR3YXJlIEZvdW5kYXRpb24xEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xMDEw
-MDgyMzAxNTZaFw0yMDEwMDUyMzAxNTZaMF8xCzAJBgNVBAYTAlhZMRcwFQYDVQQH
-Ew5DYXN0bGUgQW50aHJheDEjMCEGA1UEChMaUHl0aG9uIFNvZnR3YXJlIEZvdW5k
-YXRpb24xEjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
-gYkCgYEA21vT5isq7F68amYuuNpSFlKDPrMUCa4YWYqZRt2OZ+/3NKaZ2xAiSwr7
-6MrQF70t5nLbSPpqE5+5VrS58SY+g/sXLiFd6AplH1wJZwh78DofbFYXUggktFMt
-pTyiX8jtP66bkcPkDADA089RI1TQR6Ca+n7HFa7c1fabVV6i3zkCAwEAAaMYMBYw
-FAYDVR0RBA0wC4IJbG9jYWxob3N0MA0GCSqGSIb3DQEBBQUAA4GBAHPctQBEQ4wd
-BJ6+JcpIraopLn8BGhbjNWj40mmRqWB/NAWF6M5ne7KpGAu7tLeG4hb1zLaldK8G
-lxy2GPSRF6LFS48dpEj2HbMv2nvv6xxalDMJ9+DicWgAKTQ6bcX2j3GUkCR0g/T1
-CRlNBAAlvhKzO7Clpf9l0YKBEfraJByX
------END CERTIFICATE-----
diff --git a/Lib/test/test_asyncio/ssl_key.pem b/Lib/test/test_asyncio/ssl_key.pem
deleted file mode 100644 (file)
index 3fd3bbd..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANtb0+YrKuxevGpm
-LrjaUhZSgz6zFAmuGFmKmUbdjmfv9zSmmdsQIksK++jK0Be9LeZy20j6ahOfuVa0
-ufEmPoP7Fy4hXegKZR9cCWcIe/A6H2xWF1IIJLRTLaU8ol/I7T+um5HD5AwAwNPP
-USNU0Eegmvp+xxWu3NX2m1Veot85AgMBAAECgYA3ZdZ673X0oexFlq7AAmrutkHt
-CL7LvwrpOiaBjhyTxTeSNWzvtQBkIU8DOI0bIazA4UreAFffwtvEuPmonDb3F+Iq
-SMAu42XcGyVZEl+gHlTPU9XRX7nTOXVt+MlRRRxL6t9GkGfUAXI3XxJDXW3c0vBK
-UL9xqD8cORXOfE06rQJBAP8mEX1ERkR64Ptsoe4281vjTlNfIbs7NMPkUnrn9N/Y
-BLhjNIfQ3HFZG8BTMLfX7kCS9D593DW5tV4Z9BP/c6cCQQDcFzCcVArNh2JSywOQ
-ZfTfRbJg/Z5Lt9Fkngv1meeGNPgIMLN8Sg679pAOOWmzdMO3V706rNPzSVMME7E5
-oPIfAkEA8pDddarP5tCvTTgUpmTFbakm0KoTZm2+FzHcnA4jRh+XNTjTOv98Y6Ik
-eO5d1ZnKXseWvkZncQgxfdnMqqpj5wJAcNq/RVne1DbYlwWchT2Si65MYmmJ8t+F
-0mcsULqjOnEMwf5e+ptq5LzwbyrHZYq5FNk7ocufPv/ZQrcSSC+cFwJBAKvOJByS
-x56qyGeZLOQlWS2JS3KJo59XuLFGqcbgN9Om9xFa41Yb4N9NvplFivsvZdw3m1Q/
-SPIXQuT8RMPDVNQ=
------END PRIVATE KEY-----
index 59d321e..559ed3e 100644 (file)
@@ -272,7 +272,7 @@ class BaseEventLoopTests(test_utils.TestCase):
         loop.set_debug(debug)
         if debug:
             msg = ("Non-thread-safe operation invoked on an event loop other "
-                  "than the current one")
+                   "than the current one")
             with self.assertRaisesRegex(RuntimeError, msg):
                 loop.call_soon(cb)
             with self.assertRaisesRegex(RuntimeError, msg):
@@ -2056,5 +2056,31 @@ class BaseLoopSockSendfileTests(test_utils.TestCase):
             self.run_loop(self.loop.sock_sendfile(sock, self.file, -1))
 
 
+class TestSelectorUtils(test_utils.TestCase):
+    def check_set_nodelay(self, sock):
+        opt = sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY)
+        self.assertFalse(opt)
+
+        base_events._set_nodelay(sock)
+
+        opt = sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY)
+        self.assertTrue(opt)
+
+    @unittest.skipUnless(hasattr(socket, 'TCP_NODELAY'),
+                         'need socket.TCP_NODELAY')
+    def test_set_nodelay(self):
+        sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM,
+                             proto=socket.IPPROTO_TCP)
+        with sock:
+            self.check_set_nodelay(sock)
+
+        sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM,
+                             proto=socket.IPPROTO_TCP)
+        with sock:
+            sock.setblocking(False)
+            self.check_set_nodelay(sock)
+
+
+
 if __name__ == '__main__':
     unittest.main()
index 68b6ee9..e94af28 100644 (file)
@@ -15,7 +15,6 @@ from asyncio.selector_events import BaseSelectorEventLoop
 from asyncio.selector_events import _SelectorTransport
 from asyncio.selector_events import _SelectorSocketTransport
 from asyncio.selector_events import _SelectorDatagramTransport
-from asyncio.selector_events import _set_nodelay
 from test.test_asyncio import utils as test_utils
 
 
@@ -1746,30 +1745,5 @@ class SelectorDatagramTransportTests(test_utils.TestCase):
             exc_info=(ConnectionRefusedError, MOCK_ANY, MOCK_ANY))
 
 
-class TestSelectorUtils(test_utils.TestCase):
-    def check_set_nodelay(self, sock):
-        opt = sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY)
-        self.assertFalse(opt)
-
-        _set_nodelay(sock)
-
-        opt = sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY)
-        self.assertTrue(opt)
-
-    @unittest.skipUnless(hasattr(socket, 'TCP_NODELAY'),
-                         'need socket.TCP_NODELAY')
-    def test_set_nodelay(self):
-        sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM,
-                             proto=socket.IPPROTO_TCP)
-        with sock:
-            self.check_set_nodelay(sock)
-
-        sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM,
-                             proto=socket.IPPROTO_TCP)
-        with sock:
-            sock.setblocking(False)
-            self.check_set_nodelay(sock)
-
-
 if __name__ == '__main__':
     unittest.main()
index 46a1523..44ae3b6 100644 (file)
@@ -2,6 +2,7 @@
 
 import logging
 import socket
+import sys
 import unittest
 from unittest import mock
 try:
@@ -426,6 +427,12 @@ class BaseStartTLS(func_tests.FunctionalTestCaseMixin):
 
         server_context = test_utils.simple_server_sslcontext()
         client_context = test_utils.simple_client_sslcontext()
+        if sys.platform.startswith('freebsd'):
+            # bpo-35031: Some FreeBSD buildbots fail to run this test
+            # as the eof was not being received by the server if the payload
+            # size is not big enough. This behaviour only appears if the
+            # client is using TLS1.3.
+            client_context.options |= ssl.OP_NO_TLSv1_3
 
         def client(sock, addr):
             sock.settimeout(self.TIMEOUT)
index e7438d4..a148f16 100644 (file)
@@ -40,7 +40,7 @@ def data_file(filename):
         fullname = os.path.join(support.TEST_HOME_DIR, filename)
         if os.path.isfile(fullname):
             return fullname
-    fullname = os.path.join(os.path.dirname(__file__), filename)
+    fullname = os.path.join(os.path.dirname(__file__), '..', filename)
     if os.path.isfile(fullname):
         return fullname
     raise FileNotFoundError(filename)
@@ -156,14 +156,8 @@ class SSLWSGIServerMixin:
         # contains the ssl key and certificate files) differs
         # between the stdlib and stand-alone asyncio.
         # Prefer our own if we can find it.
-        here = os.path.join(os.path.dirname(__file__), '..', 'tests')
-        if not os.path.isdir(here):
-            here = os.path.join(os.path.dirname(os.__file__),
-                                'test', 'test_asyncio')
-        keyfile = os.path.join(here, 'ssl_key.pem')
-        certfile = os.path.join(here, 'ssl_cert.pem')
         context = ssl.SSLContext()
-        context.load_cert_chain(certfile, keyfile)
+        context.load_cert_chain(ONLYCERT, ONLYKEY)
 
         ssock = context.wrap_socket(request, server_side=True)
         try:
index 616c3a8..55f42e2 100644 (file)
@@ -549,11 +549,11 @@ def create_modules(modules):
 def break_in_func(funcname, fname=__file__, temporary=False, cond=None):
     return 'break', (fname, None, temporary, cond, funcname)
 
-TEST_MODULE = 'test_module'
+TEST_MODULE = 'test_module_for_bdb'
 TEST_MODULE_FNAME = TEST_MODULE + '.py'
 def tfunc_import():
-    import test_module
-    test_module.main()
+    import test_module_for_bdb
+    test_module_for_bdb.main()
 
 def tfunc_main():
     lno = 2
@@ -971,9 +971,9 @@ class RunTestCase(BaseTestCase):
                 ('return', 3, 'main'),     ('step', ),
                 ('return', 1, '<module>'), ('quit', ),
             ]
-            import test_module
+            import test_module_for_bdb
             with TracerRun(self) as tracer:
-                tracer.runeval('test_module.main()', globals(), locals())
+                tracer.runeval('test_module_for_bdb.main()', globals(), locals())
 
 class IssuesTestCase(BaseTestCase):
     """Test fixed bdb issues."""
@@ -983,7 +983,7 @@ class IssuesTestCase(BaseTestCase):
         # Check that the tracer does step into the caller frame when the
         # trace function is not set in that frame.
         code_1 = """
-            from test_module_2 import func
+            from test_module_for_bdb_2 import func
             def main():
                 func()
                 lno = 5
@@ -994,12 +994,12 @@ class IssuesTestCase(BaseTestCase):
         """
         modules = {
             TEST_MODULE: code_1,
-            'test_module_2': code_2,
+            'test_module_for_bdb_2': code_2,
         }
         with create_modules(modules):
             self.expect_set = [
                 ('line', 2, 'tfunc_import'),
-                    break_in_func('func', 'test_module_2.py'),
+                    break_in_func('func', 'test_module_for_bdb_2.py'),
                 ('None', 2, 'tfunc_import'),      ('continue', ),
                 ('line', 3, 'func', ({1:1}, [])), ('step', ),
                 ('return', 3, 'func'),            ('step', ),
index e7202cd..7c9768a 100644 (file)
@@ -1600,7 +1600,7 @@ class TestBreakpoint(unittest.TestCase):
     @unittest.skipIf(sys.flags.ignore_environment, '-E was given')
     def test_envar_unimportable(self):
         for envar in (
-                '.', '..', '.foo', 'foo.', '.int', 'int.'
+                '.', '..', '.foo', 'foo.', '.int', 'int.',
                 'nosuchbuiltin',
                 'nosuchmodule.nosuchcallable',
                 ):
index 1408fb4..145411e 100644 (file)
@@ -69,11 +69,66 @@ class BaseBytesTest:
         self.assertRaises(IndexError, lambda: b[-sys.maxsize-2])
         self.assertRaises(IndexError, lambda: b[-10**100])
 
+    def test_from_iterable(self):
+        b = self.type2test(range(256))
+        self.assertEqual(len(b), 256)
+        self.assertEqual(list(b), list(range(256)))
+
+        # Non-sequence iterable.
+        b = self.type2test({42})
+        self.assertEqual(b, b"*")
+        b = self.type2test({43, 45})
+        self.assertIn(tuple(b), {(43, 45), (45, 43)})
+
+        # Iterator that has a __length_hint__.
+        b = self.type2test(iter(range(256)))
+        self.assertEqual(len(b), 256)
+        self.assertEqual(list(b), list(range(256)))
+
+        # Iterator that doesn't have a __length_hint__.
+        b = self.type2test(i for i in range(256) if i % 2)
+        self.assertEqual(len(b), 128)
+        self.assertEqual(list(b), list(range(256))[1::2])
+
+        # Sequence without __iter__.
+        class S:
+            def __getitem__(self, i):
+                return (1, 2, 3)[i]
+        b = self.type2test(S())
+        self.assertEqual(b, b"\x01\x02\x03")
+
+    def test_from_tuple(self):
+        # There is a special case for tuples.
+        b = self.type2test(tuple(range(256)))
+        self.assertEqual(len(b), 256)
+        self.assertEqual(list(b), list(range(256)))
+        b = self.type2test((1, 2, 3))
+        self.assertEqual(b, b"\x01\x02\x03")
+
     def test_from_list(self):
-        ints = list(range(256))
-        b = self.type2test(i for i in ints)
+        # There is a special case for lists.
+        b = self.type2test(list(range(256)))
         self.assertEqual(len(b), 256)
-        self.assertEqual(list(b), ints)
+        self.assertEqual(list(b), list(range(256)))
+        b = self.type2test([1, 2, 3])
+        self.assertEqual(b, b"\x01\x02\x03")
+
+    def test_from_mutating_list(self):
+        # Issue #34973: Crash in bytes constructor with mutating list.
+        class X:
+            def __index__(self):
+                a.clear()
+                return 42
+        a = [X(), X()]
+        self.assertEqual(bytes(a), b'*')
+
+        class Y:
+            def __index__(self):
+                if len(a) < 1000:
+                    a.append(self)
+                return 42
+        a = [Y()]
+        self.assertEqual(bytes(a), b'*' * 1000)  # should not crash
 
     def test_from_index(self):
         b = self.type2test([Indexable(), Indexable(1), Indexable(254),
@@ -85,9 +140,11 @@ class BaseBytesTest:
     def test_from_buffer(self):
         a = self.type2test(array.array('B', [1, 2, 3]))
         self.assertEqual(a, b"\x01\x02\x03")
+        a = self.type2test(b"\x01\x02\x03")
+        self.assertEqual(a, b"\x01\x02\x03")
 
-        # http://bugs.python.org/issue29159
-        # Fallback when __index__ raises exception other than OverflowError
+        # Issues #29159 and #34974.
+        # Fallback when __index__ raises a TypeError
         class B(bytes):
             def __index__(self):
                 raise TypeError
@@ -144,6 +201,20 @@ class BaseBytesTest:
         except (OverflowError, MemoryError):
             pass
 
+    def test_constructor_exceptions(self):
+        # Issue #34974: bytes and bytearray constructors replace unexpected
+        # exceptions.
+        class BadInt:
+            def __index__(self):
+                1/0
+        self.assertRaises(ZeroDivisionError, self.type2test, BadInt())
+        self.assertRaises(ZeroDivisionError, self.type2test, [BadInt()])
+
+        class BadIterable:
+            def __iter__(self):
+                1/0
+        self.assertRaises(ZeroDivisionError, self.type2test, BadIterable())
+
     def test_compare(self):
         b1 = self.type2test([1, 2, 3])
         b2 = self.type2test([1, 2, 3])
index 1db293b..134f07a 100644 (file)
@@ -1,11 +1,12 @@
 # Tests the attempted automatic coercion of the C locale to a UTF-8 locale
 
-import unittest
 import locale
 import os
+import shutil
+import subprocess
 import sys
 import sysconfig
-import shutil
+import unittest
 from collections import namedtuple
 
 import test.support
@@ -25,6 +26,8 @@ EXPECTED_C_LOCALE_FS_ENCODING = "ascii"
 # Set our expectation for the default locale used when none is specified
 EXPECT_COERCION_IN_DEFAULT_LOCALE = True
 
+TARGET_LOCALES = ["C.UTF-8", "C.utf8", "UTF-8"]
+
 # Apply some platform dependent overrides
 if sys.platform.startswith("linux"):
     if test.support.is_android:
@@ -404,6 +407,27 @@ class LocaleCoercionTests(_LocaleHandlingTestCase):
                                       expected_warnings=[LEGACY_LOCALE_WARNING],
                                       coercion_expected=False)
 
+    def test_PYTHONCOERCECLOCALE_set_to_one(self):
+        # skip the test if the LC_CTYPE locale is C or coerced
+        old_loc = locale.setlocale(locale.LC_CTYPE, None)
+        self.addCleanup(locale.setlocale, locale.LC_CTYPE, old_loc)
+        loc = locale.setlocale(locale.LC_CTYPE, "")
+        if loc == "C":
+            self.skipTest("test requires LC_CTYPE locale different than C")
+        if loc in TARGET_LOCALES :
+            self.skipTest("coerced LC_CTYPE locale: %s" % loc)
+
+        # bpo-35336: PYTHONCOERCECLOCALE=1 must not coerce the LC_CTYPE locale
+        # if it's not equal to "C"
+        code = 'import locale; print(locale.setlocale(locale.LC_CTYPE, None))'
+        env = dict(os.environ, PYTHONCOERCECLOCALE='1')
+        cmd = subprocess.run([sys.executable, '-c', code],
+                             stdout=subprocess.PIPE,
+                             env=env,
+                             text=True)
+        self.assertEqual(cmd.stdout.rstrip(), loc)
+
+
 def test_main():
     test.support.run_unittest(
         LocaleConfigurationTests,
index 49297f4..65e0795 100644 (file)
@@ -468,6 +468,8 @@ class PyMemDebugTests(unittest.TestCase):
                  r"    The block was made by call #[0-9]+ to debug malloc/realloc.\n"
                  r"    Data at p: cb cb cb .*\n"
                  r"\n"
+                 r"Enable tracemalloc to get the memory block allocation traceback\n"
+                 r"\n"
                  r"Fatal Python error: bad trailing pad byte")
         regex = regex.format(ptr=self.PTR_REGEX)
         regex = re.compile(regex, flags=re.DOTALL)
@@ -482,6 +484,8 @@ class PyMemDebugTests(unittest.TestCase):
                  r"    The block was made by call #[0-9]+ to debug malloc/realloc.\n"
                  r"    Data at p: cb cb cb .*\n"
                  r"\n"
+                 r"Enable tracemalloc to get the memory block allocation traceback\n"
+                 r"\n"
                  r"Fatal Python error: bad ID: Allocated using API 'm', verified using API 'r'\n")
         regex = regex.format(ptr=self.PTR_REGEX)
         self.assertRegex(out, regex)
index 4f2bba1..f4e00c7 100644 (file)
@@ -391,6 +391,60 @@ Larry
         v = gen_result(data, environ)
         self.assertEqual(self._qs_result, v)
 
+    def test_max_num_fields(self):
+        # For application/x-www-form-urlencoded
+        data = '&'.join(['a=a']*11)
+        environ = {
+            'CONTENT_LENGTH': str(len(data)),
+            'CONTENT_TYPE': 'application/x-www-form-urlencoded',
+            'REQUEST_METHOD': 'POST',
+        }
+
+        with self.assertRaises(ValueError):
+            cgi.FieldStorage(
+                fp=BytesIO(data.encode()),
+                environ=environ,
+                max_num_fields=10,
+            )
+
+        # For multipart/form-data
+        data = """---123
+Content-Disposition: form-data; name="a"
+
+3
+---123
+Content-Type: application/x-www-form-urlencoded
+
+a=4
+---123
+Content-Type: application/x-www-form-urlencoded
+
+a=5
+---123--
+"""
+        environ = {
+            'CONTENT_LENGTH':   str(len(data)),
+            'CONTENT_TYPE':     'multipart/form-data; boundary=-123',
+            'QUERY_STRING':     'a=1&a=2',
+            'REQUEST_METHOD':   'POST',
+        }
+
+        # 2 GET entities
+        # 1 top level POST entities
+        # 1 entity within the second POST entity
+        # 1 entity within the third POST entity
+        with self.assertRaises(ValueError):
+            cgi.FieldStorage(
+                fp=BytesIO(data.encode()),
+                environ=environ,
+                max_num_fields=4,
+            )
+        cgi.FieldStorage(
+            fp=BytesIO(data.encode()),
+            environ=environ,
+            max_num_fields=5,
+        )
+
     def testQSAndFormData(self):
         data = """---123
 Content-Disposition: form-data; name="key2"
index 4f5af37..5ec9bbb 100644 (file)
@@ -630,8 +630,6 @@ class CmdLineTest(unittest.TestCase):
             traceback_lines = stderr.decode().splitlines()
             self.assertIn("No module named script_pkg", traceback_lines[-1])
 
-    @unittest.skipIf(sys.platform == 'darwin' and sys._framework,
-                        "test not valid for macOS framework builds")
     def test_nonexisting_script(self):
         # bpo-34783: "./python script.py" must not crash
         # if the script file doesn't exist.
@@ -639,17 +637,12 @@ class CmdLineTest(unittest.TestCase):
         #  is not the actual Python executable file name.
         script = 'nonexistingscript.py'
         self.assertFalse(os.path.exists(script))
-        # Only test the base name, since the error message can use
-        # a relative path, whereas sys.executable can be an asolution path.
-        program = os.path.basename(sys.executable)
 
         proc = spawn_python(script, text=True,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
         out, err = proc.communicate()
-        # "./python" must be in the error message:
-        # "./python: can't open file (...)"
-        self.assertIn(program, err)
+        self.assertIn(": can't open file ", err)
         self.assertNotEqual(proc.returncode, 0)
 
 
index a59a5e2..5c2de21 100644 (file)
@@ -3185,6 +3185,24 @@ class CodePageTest(unittest.TestCase):
             codec = codecs.lookup('cp123')
             self.assertEqual(codec.name, 'mbcs')
 
+    @support.bigmemtest(size=2**31, memuse=7, dry_run=False)
+    def test_large_input(self):
+        # Test input longer than INT_MAX.
+        # Input should contain undecodable bytes before and after
+        # the INT_MAX limit.
+        encoded = (b'01234567' * (2**28-1) +
+                   b'\x85\x86\xea\xeb\xec\xef\xfc\xfd\xfe\xff')
+        self.assertEqual(len(encoded), 2**31+2)
+        decoded = codecs.code_page_decode(932, encoded, 'surrogateescape', True)
+        self.assertEqual(decoded[1], len(encoded))
+        del encoded
+        self.assertEqual(len(decoded[0]), decoded[1])
+        self.assertEqual(decoded[0][:10], '0123456701')
+        self.assertEqual(decoded[0][-20:],
+                         '6701234567'
+                         '\udc85\udc86\udcea\udceb\udcec'
+                         '\udcef\udcfc\udcfd\udcfe\udcff')
+
 
 class ASCIITest(unittest.TestCase):
     def test_encode(self):
index acebdbd..5ac1c5f 100644 (file)
@@ -1,3 +1,4 @@
+import dis
 import math
 import os
 import unittest
@@ -285,7 +286,7 @@ if 1:
             'from sys import stdin)',
             'from sys import stdin, stdout,\nstderr',
             'from sys import stdin si',
-            'from sys import stdin,'
+            'from sys import stdin,',
             'from sys import (*)',
             'from sys import (stdin,, stdout, stderr)',
             'from sys import (stdin, stdout),',
@@ -621,6 +622,24 @@ if 1:
         self.check_constant(f1, frozenset({0}))
         self.assertTrue(f1(0))
 
+    # This is a regression test for a CPython specific peephole optimizer
+    # implementation bug present in a few releases.  It's assertion verifies
+    # that peephole optimization was actually done though that isn't an
+    # indication of the bugs presence or not (crashing is).
+    @support.cpython_only
+    def test_peephole_opt_unreachable_code_array_access_in_bounds(self):
+        """Regression test for issue35193 when run under clang msan."""
+        def unused_code_at_end():
+            return 3
+            raise RuntimeError("unreachable")
+        # The above function definition will trigger the out of bounds
+        # bug in the peephole optimizer as it scans opcodes past the
+        # RETURN_VALUE opcode.  This does not always crash an interpreter.
+        # When you build with the clang memory sanitizer it reliably aborts.
+        self.assertEqual(
+            'RETURN_VALUE',
+            list(dis.get_instructions(unused_code_at_end))[-1].opname)
+
     def test_dont_merge_constants(self):
         # Issue #25843: compile() must not merge constants which are equal
         # but have a different type.
index 38d7b99..2e25523 100644 (file)
@@ -22,7 +22,11 @@ except ImportError:
 from test import support
 from test.support import script_helper
 
-class CompileallTests(unittest.TestCase):
+from .test_py_compile import without_source_date_epoch
+from .test_py_compile import SourceDateEpochTestMeta
+
+
+class CompileallTestsBase:
 
     def setUp(self):
         self.directory = tempfile.mkdtemp()
@@ -46,7 +50,7 @@ class CompileallTests(unittest.TestCase):
         with open(self.bad_source_path, 'w') as file:
             file.write('x (\n')
 
-    def data(self):
+    def timestamp_metadata(self):
         with open(self.bc_path, 'rb') as file:
             data = file.read(12)
         mtime = int(os.stat(self.source_path).st_mtime)
@@ -57,16 +61,18 @@ class CompileallTests(unittest.TestCase):
     def recreation_check(self, metadata):
         """Check that compileall recreates bytecode when the new metadata is
         used."""
+        if os.environ.get('SOURCE_DATE_EPOCH'):
+            raise unittest.SkipTest('SOURCE_DATE_EPOCH is set')
         py_compile.compile(self.source_path)
-        self.assertEqual(*self.data())
+        self.assertEqual(*self.timestamp_metadata())
         with open(self.bc_path, 'rb') as file:
             bc = file.read()[len(metadata):]
         with open(self.bc_path, 'wb') as file:
             file.write(metadata)
             file.write(bc)
-        self.assertNotEqual(*self.data())
+        self.assertNotEqual(*self.timestamp_metadata())
         compileall.compile_dir(self.directory, force=False, quiet=True)
-        self.assertTrue(*self.data())
+        self.assertTrue(*self.timestamp_metadata())
 
     def test_mtime(self):
         # Test a change in mtime leads to a new .pyc.
@@ -161,7 +167,7 @@ class CompileallTests(unittest.TestCase):
         self.assertRegex(line, r'Listing ([^WindowsPath|PosixPath].*)')
         self.assertTrue(os.path.isfile(self.bc_path))
 
-    @mock.patch('compileall.ProcessPoolExecutor')
+    @mock.patch('concurrent.futures.ProcessPoolExecutor')
     def test_compile_pool_called(self, pool_mock):
         compileall.compile_dir(self.directory, quiet=True, workers=5)
         self.assertTrue(pool_mock.called)
@@ -171,24 +177,39 @@ class CompileallTests(unittest.TestCase):
                                     "workers must be greater or equal to 0"):
             compileall.compile_dir(self.directory, workers=-1)
 
-    @mock.patch('compileall.ProcessPoolExecutor')
+    @mock.patch('concurrent.futures.ProcessPoolExecutor')
     def test_compile_workers_cpu_count(self, pool_mock):
         compileall.compile_dir(self.directory, quiet=True, workers=0)
         self.assertEqual(pool_mock.call_args[1]['max_workers'], None)
 
-    @mock.patch('compileall.ProcessPoolExecutor')
+    @mock.patch('concurrent.futures.ProcessPoolExecutor')
     @mock.patch('compileall.compile_file')
     def test_compile_one_worker(self, compile_file_mock, pool_mock):
         compileall.compile_dir(self.directory, quiet=True)
         self.assertFalse(pool_mock.called)
         self.assertTrue(compile_file_mock.called)
 
-    @mock.patch('compileall.ProcessPoolExecutor', new=None)
+    @mock.patch('concurrent.futures.ProcessPoolExecutor', new=None)
     @mock.patch('compileall.compile_file')
     def test_compile_missing_multiprocessing(self, compile_file_mock):
         compileall.compile_dir(self.directory, quiet=True, workers=5)
         self.assertTrue(compile_file_mock.called)
 
+
+class CompileallTestsWithSourceEpoch(CompileallTestsBase,
+                                     unittest.TestCase,
+                                     metaclass=SourceDateEpochTestMeta,
+                                     source_date_epoch=True):
+    pass
+
+
+class CompileallTestsWithoutSourceEpoch(CompileallTestsBase,
+                                        unittest.TestCase,
+                                        metaclass=SourceDateEpochTestMeta,
+                                        source_date_epoch=False):
+    pass
+
+
 class EncodingTest(unittest.TestCase):
     """Issue 6716: compileall should escape source code when printing errors
     to stdout."""
@@ -212,7 +233,7 @@ class EncodingTest(unittest.TestCase):
             sys.stdout = orig_stdout
 
 
-class CommandLineTests(unittest.TestCase):
+class CommandLineTestsBase:
     """Test compileall's CLI."""
 
     @classmethod
@@ -285,6 +306,7 @@ class CommandLineTests(unittest.TestCase):
         self.assertNotCompiled(self.initfn)
         self.assertNotCompiled(self.barfn)
 
+    @without_source_date_epoch  # timestamp invalidation test
     def test_no_args_respects_force_flag(self):
         self._skip_if_sys_path_not_writable()
         bazfn = script_helper.make_script(self.directory, 'baz', '')
@@ -353,6 +375,7 @@ class CommandLineTests(unittest.TestCase):
         self.assertTrue(os.path.exists(self.pkgdir_cachedir))
         self.assertFalse(os.path.exists(cachecachedir))
 
+    @without_source_date_epoch  # timestamp invalidation test
     def test_force(self):
         self.assertRunOK('-q', self.pkgdir)
         pycpath = importlib.util.cache_from_source(self.barfn)
@@ -556,5 +579,20 @@ class CommandLineTests(unittest.TestCase):
             self.assertEqual(compile_dir.call_args[-1]['workers'], None)
 
 
+class CommmandLineTestsWithSourceEpoch(CommandLineTestsBase,
+                                       unittest.TestCase,
+                                       metaclass=SourceDateEpochTestMeta,
+                                       source_date_epoch=True):
+    pass
+
+
+class CommmandLineTestsNoSourceEpoch(CommandLineTestsBase,
+                                     unittest.TestCase,
+                                     metaclass=SourceDateEpochTestMeta,
+                                     source_date_epoch=False):
+    pass
+
+
+
 if __name__ == "__main__":
     unittest.main()
index 1430d22..5c4ec5b 100644 (file)
@@ -2,6 +2,7 @@
 
 import sys
 from test.support import run_unittest, TESTFN, unlink
+import unittest
 
 # rip off all interesting stuff from test_profile
 import cProfile
@@ -50,8 +51,14 @@ class CProfileTest(ProfileTest):
         assert_python_ok('-m', 'cProfile', '-m', 'timeit', '-n', '1')
 
 
+class TestCommandLine(unittest.TestCase):
+    def test_sort(self):
+        rc, out, err = assert_python_failure('-m', 'cProfile', '-s', 'demo')
+        self.assertGreater(rc, 0)
+        self.assertIn(b"option -s: invalid choice: 'demo'", err)
+
 def test_main():
-    run_unittest(CProfileTest)
+    run_unittest(CProfileTest, TestCommandLine)
 
 def main():
     if '-r' not in sys.argv:
index 6efe785..ff6060c 100755 (executable)
@@ -3169,6 +3169,92 @@ class TestReplace(unittest.TestCase):
             replace(c, x=3)
         c = replace(c, x=3, y=5)
         self.assertEqual(c.x, 15)
+
+    def test_recursive_repr(self):
+        @dataclass
+        class C:
+            f: "C"
+
+        c = C(None)
+        c.f = c
+        self.assertEqual(repr(c), "TestReplace.test_recursive_repr.<locals>.C(f=...)")
+
+    def test_recursive_repr_two_attrs(self):
+        @dataclass
+        class C:
+            f: "C"
+            g: "C"
+
+        c = C(None, None)
+        c.f = c
+        c.g = c
+        self.assertEqual(repr(c), "TestReplace.test_recursive_repr_two_attrs"
+                                  ".<locals>.C(f=..., g=...)")
+
+    def test_recursive_repr_indirection(self):
+        @dataclass
+        class C:
+            f: "D"
+
+        @dataclass
+        class D:
+            f: "C"
+
+        c = C(None)
+        d = D(None)
+        c.f = d
+        d.f = c
+        self.assertEqual(repr(c), "TestReplace.test_recursive_repr_indirection"
+                                  ".<locals>.C(f=TestReplace.test_recursive_repr_indirection"
+                                  ".<locals>.D(f=...))")
+
+    def test_recursive_repr_indirection_two(self):
+        @dataclass
+        class C:
+            f: "D"
+
+        @dataclass
+        class D:
+            f: "E"
+
+        @dataclass
+        class E:
+            f: "C"
+
+        c = C(None)
+        d = D(None)
+        e = E(None)
+        c.f = d
+        d.f = e
+        e.f = c
+        self.assertEqual(repr(c), "TestReplace.test_recursive_repr_indirection_two"
+                                  ".<locals>.C(f=TestReplace.test_recursive_repr_indirection_two"
+                                  ".<locals>.D(f=TestReplace.test_recursive_repr_indirection_two"
+                                  ".<locals>.E(f=...)))")
+
+    def test_recursive_repr_two_attrs(self):
+        @dataclass
+        class C:
+            f: "C"
+            g: "C"
+
+        c = C(None, None)
+        c.f = c
+        c.g = c
+        self.assertEqual(repr(c), "TestReplace.test_recursive_repr_two_attrs"
+                                  ".<locals>.C(f=..., g=...)")
+
+    def test_recursive_repr_misc_attrs(self):
+        @dataclass
+        class C:
+            f: "C"
+            g: int
+
+        c = C(None, 1)
+        c.f = c
+        self.assertEqual(repr(c), "TestReplace.test_recursive_repr_misc_attrs"
+                                  ".<locals>.C(f=..., g=1)")
+
     ## def test_initvar(self):
     ##     @dataclass
     ##     class C:
index 58bde54..1f37b53 100644 (file)
@@ -1172,10 +1172,10 @@ class FormatTest(unittest.TestCase):
         decimal_point = locale.localeconv()['decimal_point']
         thousands_sep = locale.localeconv()['thousands_sep']
         if decimal_point != '\u066b':
-            self.skipTest('inappropriate decimal point separator'
+            self.skipTest('inappropriate decimal point separator '
                           '({!a} not {!a})'.format(decimal_point, '\u066b'))
         if thousands_sep != '\u066c':
-            self.skipTest('inappropriate thousands separator'
+            self.skipTest('inappropriate thousands separator '
                           '({!a} not {!a})'.format(thousands_sep, '\u066c'))
 
         self.assertEqual(format(Decimal('100000000.123'), 'n'),
index 25f86d3..f61efa3 100644 (file)
@@ -1,5 +1,7 @@
 import os
 import signal
+import subprocess
+import sys
 import unittest
 
 from test import support
@@ -15,7 +17,20 @@ class EINTRTests(unittest.TestCase):
         # thread (for reliable signal delivery).
         tester = support.findfile("eintr_tester.py", subdir="eintrdata")
         # use -u to try to get the full output if the test hangs or crash
-        script_helper.assert_python_ok("-u", tester)
+        args = ["-u", tester, "-v"]
+        if support.verbose:
+            print()
+            print("--- run eintr_tester.py ---", flush=True)
+            # In verbose mode, the child process inherit stdout and stdout,
+            # to see output in realtime and reduce the risk of loosing output.
+            args = [sys.executable, "-E", "-X", "faulthandler", *args]
+            proc = subprocess.run(args)
+            print(f"--- eintr_tester.py completed: "
+                  f"exit code {proc.returncode} ---", flush=True)
+            if proc.returncode:
+                self.fail("eintr_tester.py failed")
+        else:
+            script_helper.assert_python_ok("-u", tester, "-v")
 
 
 if __name__ == "__main__":
index 6dcb3bb..4e3c3f3 100644 (file)
@@ -75,6 +75,7 @@ class LocaltimeTests(unittest.TestCase):
         t2 = utils.localtime(t1)
         self.assertEqual(t1, t2)
 
+    @test.support.run_with_tz('Europe/Minsk')
     def test_localtime_daylight_true_dst_true(self):
         test.support.patch(self, time, 'daylight', True)
         t0 = datetime.datetime(2012, 3, 12, 1, 1)
@@ -82,6 +83,7 @@ class LocaltimeTests(unittest.TestCase):
         t2 = utils.localtime(t1)
         self.assertEqual(t1, t2)
 
+    @test.support.run_with_tz('Europe/Minsk')
     def test_localtime_daylight_false_dst_true(self):
         test.support.patch(self, time, 'daylight', False)
         t0 = datetime.datetime(2012, 3, 12, 1, 1)
index 2927458..de3c303 100644 (file)
@@ -3,10 +3,15 @@ from test import support
 import unittest
 
 from collections import namedtuple
+import json
 import os
 import re
 import subprocess
 import sys
+import textwrap
+
+
+MS_WINDOWS = (os.name == 'nt')
 
 
 class EmbeddingTestsMixin:
@@ -14,7 +19,7 @@ class EmbeddingTestsMixin:
         here = os.path.abspath(__file__)
         basepath = os.path.dirname(os.path.dirname(os.path.dirname(here)))
         exename = "_testembed"
-        if sys.platform.startswith("win"):
+        if MS_WINDOWS:
             ext = ("_d" if "_d" in sys.executable else "") + ".exe"
             exename += ext
             exepath = os.path.dirname(sys.executable)
@@ -36,7 +41,7 @@ class EmbeddingTestsMixin:
         """Runs a test in the embedded interpreter"""
         cmd = [self.test_exe]
         cmd.extend(args)
-        if env is not None and sys.platform == 'win32':
+        if env is not None and MS_WINDOWS:
             # Windows requires at least the SYSTEMROOT environment variable to
             # start Python.
             env = env.copy()
@@ -197,7 +202,7 @@ class EmbeddingTests(EmbeddingTestsMixin, unittest.TestCase):
         """
         env = dict(os.environ, PYTHONPATH=os.pathsep.join(sys.path))
         out, err = self.run_embedded_interpreter("pre_initialization_api", env=env)
-        if sys.platform == "win32":
+        if MS_WINDOWS:
             expected_path = self.test_exe
         else:
             expected_path = os.path.join(os.getcwd(), "spam")
@@ -251,12 +256,23 @@ class EmbeddingTests(EmbeddingTestsMixin, unittest.TestCase):
 
 class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
     maxDiff = 4096
-    DEFAULT_CONFIG = {
+    UTF8_MODE_ERRORS = ('surrogatepass' if MS_WINDOWS else 'surrogateescape')
+
+    # core config
+    UNTESTED_CORE_CONFIG = (
+        # FIXME: untested core configuration variables
+        'dll_path',
+        'executable',
+        'module_search_paths',
+    )
+    # Mark config which should be get by get_default_config()
+    GET_DEFAULT_CONFIG = object()
+    DEFAULT_CORE_CONFIG = {
         'install_signal_handlers': 1,
-        'Py_IgnoreEnvironmentFlag': 0,
+        'ignore_environment': 0,
         'use_hash_seed': 0,
         'hash_seed': 0,
-        'allocator': '(null)',
+        'allocator': None,
         'dev_mode': 0,
         'faulthandler': 0,
         'tracemalloc': 0,
@@ -265,35 +281,180 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
         'show_alloc_count': 0,
         'dump_refs': 0,
         'malloc_stats': 0,
-        'utf8_mode': 0,
 
+        'utf8_mode': 0,
         'coerce_c_locale': 0,
         'coerce_c_locale_warn': 0,
 
         'program_name': './_testembed',
-        'argc': 0,
-        'argv': '[]',
-        'program': '(null)',
+        'argv': [],
+        'program': None,
 
-        'Py_IsolatedFlag': 0,
-        'Py_NoSiteFlag': 0,
+        'xoptions': [],
+        'warnoptions': [],
+
+        'module_search_path_env': None,
+        'home': None,
+
+        'prefix': GET_DEFAULT_CONFIG,
+        'base_prefix': GET_DEFAULT_CONFIG,
+        'exec_prefix': GET_DEFAULT_CONFIG,
+        'base_exec_prefix': GET_DEFAULT_CONFIG,
+
+        '_disable_importlib': 0,
+    }
+
+    # main config
+    UNTESTED_MAIN_CONFIG = (
+        # FIXME: untested main configuration variables
+        'module_search_path',
+    )
+    COPY_MAIN_CONFIG = (
+        # Copy core config to main config for expected values
+        'argv',
+        'base_exec_prefix',
+        'base_prefix',
+        'exec_prefix',
+        'executable',
+        'install_signal_handlers',
+        'prefix',
+        'warnoptions',
+        # xoptions is created from core_config in check_main_config()
+    )
+
+    # global config
+    UNTESTED_GLOBAL_CONFIG = (
+        # Py_HasFileSystemDefaultEncoding value depends on the LC_CTYPE locale
+        # and the platform. It is complex to test it, and it's value doesn't
+        # really matter.
+        'Py_HasFileSystemDefaultEncoding',
+    )
+    DEFAULT_GLOBAL_CONFIG = {
         'Py_BytesWarningFlag': 0,
+        'Py_DebugFlag': 0,
+        'Py_DontWriteBytecodeFlag': 0,
+        'Py_FileSystemDefaultEncodeErrors': GET_DEFAULT_CONFIG,
+        'Py_FileSystemDefaultEncoding': GET_DEFAULT_CONFIG,
+        'Py_FrozenFlag': 0,
+        'Py_HashRandomizationFlag': 1,
         'Py_InspectFlag': 0,
         'Py_InteractiveFlag': 0,
+        'Py_IsolatedFlag': 0,
+        'Py_NoSiteFlag': 0,
+        'Py_NoUserSiteDirectory': 0,
         'Py_OptimizeFlag': 0,
-        'Py_DebugFlag': 0,
-        'Py_DontWriteBytecodeFlag': 0,
-        'Py_VerboseFlag': 0,
         'Py_QuietFlag': 0,
-        'Py_NoUserSiteDirectory': 0,
         'Py_UnbufferedStdioFlag': 0,
-
-        '_disable_importlib': 0,
-        'Py_FrozenFlag': 0,
+        'Py_VerboseFlag': 0,
     }
-
-    def check_config(self, testname, expected):
+    if MS_WINDOWS:
+        DEFAULT_GLOBAL_CONFIG.update({
+            'Py_LegacyWindowsFSEncodingFlag': 0,
+            'Py_LegacyWindowsStdioFlag': 0,
+        })
+    COPY_GLOBAL_CONFIG = [
+        # Copy core config to global config for expected values
+        # True means that the core config value is inverted (0 => 1 and 1 => 0)
+        ('Py_IgnoreEnvironmentFlag', 'ignore_environment'),
+        ('Py_UTF8Mode', 'utf8_mode'),
+    ]
+
+    def main_xoptions(self, xoptions_list):
+        xoptions = {}
+        for opt in xoptions_list:
+            if '=' in opt:
+                key, value = opt.split('=', 1)
+                xoptions[key] = value
+            else:
+                xoptions[opt] = True
+        return xoptions
+
+    def check_main_config(self, config):
+        core_config = config['core_config']
+        main_config = config['main_config']
+
+        # main config
+        for key in self.UNTESTED_MAIN_CONFIG:
+            del main_config[key]
+
+        expected = {}
+        for key in self.COPY_MAIN_CONFIG:
+            expected[key] = core_config[key]
+        expected['xoptions'] = self.main_xoptions(core_config['xoptions'])
+        self.assertEqual(main_config, expected)
+
+    def get_expected_config(self, expected_core, expected_global, env):
+        expected_core = dict(self.DEFAULT_CORE_CONFIG, **expected_core)
+        expected_global = dict(self.DEFAULT_GLOBAL_CONFIG, **expected_global)
+
+        code = textwrap.dedent('''
+            import json
+            import sys
+
+            data = {
+                'prefix': sys.prefix,
+                'base_prefix': sys.base_prefix,
+                'exec_prefix': sys.exec_prefix,
+                'base_exec_prefix': sys.base_exec_prefix,
+                'Py_FileSystemDefaultEncoding': sys.getfilesystemencoding(),
+                'Py_FileSystemDefaultEncodeErrors': sys.getfilesystemencodeerrors(),
+            }
+
+            data = json.dumps(data)
+            data = data.encode('utf-8')
+            sys.stdout.buffer.write(data)
+            sys.stdout.buffer.flush()
+        ''')
+
+        # Use -S to not import the site module: get the proper configuration
+        # when test_embed is run from a venv (bpo-35313)
+        args = (sys.executable, '-S', '-c', code)
+        env = dict(env)
+        if not expected_global['Py_IsolatedFlag']:
+            env['PYTHONCOERCECLOCALE'] = '0'
+            env['PYTHONUTF8'] = '0'
+        proc = subprocess.run(args, env=env,
+                              stdout=subprocess.PIPE,
+                              stderr=subprocess.STDOUT)
+        if proc.returncode:
+            raise Exception(f"failed to get the default config: "
+                            f"stdout={proc.stdout!r} stderr={proc.stderr!r}")
+        stdout = proc.stdout.decode('utf-8')
+        config = json.loads(stdout)
+
+        for key, value in expected_core.items():
+            if value is self.GET_DEFAULT_CONFIG:
+                expected_core[key] = config[key]
+        for key, value in expected_global.items():
+            if value is self.GET_DEFAULT_CONFIG:
+                expected_global[key] = config[key]
+        return (expected_core, expected_global)
+
+    def check_core_config(self, config, expected):
+        core_config = dict(config['core_config'])
+        for key in self.UNTESTED_CORE_CONFIG:
+            core_config.pop(key, None)
+        self.assertEqual(core_config, expected)
+
+    def check_global_config(self, config, expected, env):
+        core_config = config['core_config']
+
+        for item in self.COPY_GLOBAL_CONFIG:
+            if len(item) == 3:
+                global_key, core_key, opposite = item
+                expected[global_key] = 0 if core_config[core_key] else 1
+            else:
+                global_key, core_key = item
+                expected[global_key] = core_config[core_key]
+
+        global_config = dict(config['global_config'])
+        for key in self.UNTESTED_GLOBAL_CONFIG:
+            del global_config[key]
+        self.assertEqual(global_config, expected)
+
+    def check_config(self, testname, expected_core, expected_global):
         env = dict(os.environ)
+        # Remove PYTHON* environment variables to get deterministic environment
         for key in list(env):
             if key.startswith('PYTHON'):
                 del env[key]
@@ -301,42 +462,43 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
         # on the current locale
         env['PYTHONCOERCECLOCALE'] = '0'
         env['PYTHONUTF8'] = '0'
+
         out, err = self.run_embedded_interpreter(testname, env=env)
         # Ignore err
+        config = json.loads(out)
 
-        expected = dict(self.DEFAULT_CONFIG, **expected)
-        for key, value in expected.items():
-            expected[key] = str(value)
-
-        config = {}
-        for line in out.splitlines():
-            key, value = line.split(' = ', 1)
-            config[key] = value
-        self.assertEqual(config, expected)
+        expected_core, expected_global = self.get_expected_config(expected_core, expected_global, env)
+        self.check_core_config(config, expected_core)
+        self.check_main_config(config)
+        self.check_global_config(config, expected_global, env)
 
     def test_init_default_config(self):
-        self.check_config("init_default_config", {})
+        self.check_config("init_default_config", {}, {})
 
     def test_init_global_config(self):
-        config = {
+        core_config = {
             'program_name': './globalvar',
-            'Py_NoSiteFlag': 1,
+            'utf8_mode': 1,
+        }
+        global_config = {
             'Py_BytesWarningFlag': 1,
+            'Py_DontWriteBytecodeFlag': 1,
+            'Py_FileSystemDefaultEncodeErrors': self.UTF8_MODE_ERRORS,
+            'Py_FileSystemDefaultEncoding': 'utf-8',
             'Py_InspectFlag': 1,
             'Py_InteractiveFlag': 1,
+            'Py_NoSiteFlag': 1,
+            'Py_NoUserSiteDirectory': 1,
             'Py_OptimizeFlag': 2,
-            'Py_DontWriteBytecodeFlag': 1,
-            'Py_VerboseFlag': 1,
             'Py_QuietFlag': 1,
-            'Py_UnbufferedStdioFlag': 1,
-            'utf8_mode': 1,
-            'Py_NoUserSiteDirectory': 1,
+            'Py_VerboseFlag': 1,
             'Py_FrozenFlag': 1,
+            'Py_UnbufferedStdioFlag': 1,
         }
-        self.check_config("init_global_config", config)
+        self.check_config("init_global_config", core_config, global_config)
 
     def test_init_from_config(self):
-        config = {
+        core_config = {
             'install_signal_handlers': 0,
             'use_hash_seed': 1,
             'hash_seed': 123,
@@ -350,14 +512,22 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
             'utf8_mode': 1,
 
             'program_name': './conf_program_name',
+            'argv': ['-c', 'pass'],
             'program': 'conf_program',
+            'xoptions': ['core_xoption1=3', 'core_xoption2=', 'core_xoption3'],
+            'warnoptions': ['default', 'error::ResourceWarning'],
 
             'faulthandler': 1,
         }
-        self.check_config("init_from_config", config)
+        global_config = {
+            'Py_FileSystemDefaultEncodeErrors': self.UTF8_MODE_ERRORS,
+            'Py_FileSystemDefaultEncoding': 'utf-8',
+            'Py_NoUserSiteDirectory': 0,
+        }
+        self.check_config("init_from_config", core_config, global_config)
 
     def test_init_env(self):
-        config = {
+        core_config = {
             'use_hash_seed': 1,
             'hash_seed': 42,
             'allocator': 'malloc_debug',
@@ -365,32 +535,38 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
             'import_time': 1,
             'malloc_stats': 1,
             'utf8_mode': 1,
+            'faulthandler': 1,
+            'dev_mode': 1,
+        }
+        global_config = {
+            'Py_DontWriteBytecodeFlag': 1,
+            'Py_FileSystemDefaultEncodeErrors': self.UTF8_MODE_ERRORS,
+            'Py_FileSystemDefaultEncoding': 'utf-8',
             'Py_InspectFlag': 1,
+            'Py_NoUserSiteDirectory': 1,
             'Py_OptimizeFlag': 2,
-            'Py_DontWriteBytecodeFlag': 1,
-            'Py_VerboseFlag': 1,
             'Py_UnbufferedStdioFlag': 1,
-            'Py_NoUserSiteDirectory': 1,
-            'faulthandler': 1,
-            'dev_mode': 1,
+            'Py_VerboseFlag': 1,
         }
-        self.check_config("init_env", config)
+        self.check_config("init_env", core_config, global_config)
 
     def test_init_dev_mode(self):
-        config = {
+        core_config = {
             'dev_mode': 1,
             'faulthandler': 1,
             'allocator': 'debug',
         }
-        self.check_config("init_dev_mode", config)
+        self.check_config("init_dev_mode", core_config, {})
 
     def test_init_isolated(self):
-        config = {
+        core_config = {
+            'ignore_environment': 1,
+        }
+        global_config = {
             'Py_IsolatedFlag': 1,
-            'Py_IgnoreEnvironmentFlag': 1,
             'Py_NoUserSiteDirectory': 1,
         }
-        self.check_config("init_isolated", config)
+        self.check_config("init_isolated", core_config, global_config)
 
 
 if __name__ == "__main__":
index 60eabbe..b221045 100644 (file)
@@ -1717,6 +1717,38 @@ class TestEnum(unittest.TestCase):
             third = auto()
         self.assertEqual([Dupes.first, Dupes.second, Dupes.third], list(Dupes))
 
+    def test_missing(self):
+        class Color(Enum):
+            red = 1
+            green = 2
+            blue = 3
+            @classmethod
+            def _missing_(cls, item):
+                if item == 'three':
+                    return cls.blue
+                elif item == 'bad return':
+                    # trigger internal error
+                    return 5
+                elif item == 'error out':
+                    raise ZeroDivisionError
+                else:
+                    # trigger not found
+                    return None
+        self.assertIs(Color('three'), Color.blue)
+        self.assertRaises(ValueError, Color, 7)
+        try:
+            Color('bad return')
+        except TypeError as exc:
+            self.assertTrue(isinstance(exc.__context__, ValueError))
+        else:
+            raise Exception('Exception not raised.')
+        try:
+            Color('error out')
+        except ZeroDivisionError as exc:
+            self.assertTrue(isinstance(exc.__context__, ValueError))
+        else:
+            raise Exception('Exception not raised.')
+
     def test_multiple_mixin(self):
         class MaxMixin:
             @classproperty
index 0f950b2..711fb69 100644 (file)
@@ -5,6 +5,7 @@
 
 import locale
 import os
+import platform
 import re
 import subprocess
 import sys
@@ -48,6 +49,10 @@ if gdb_major_version < 7:
 if not sysconfig.is_python_build():
     raise unittest.SkipTest("test_gdb only works on source builds at the moment.")
 
+if 'Clang' in platform.python_compiler() and sys.platform == 'darwin':
+    raise unittest.SkipTest("test_gdb doesn't work correctly when python is"
+                            " built with LLVM clang")
+
 # Location of custom hooks file in a repository checkout.
 checkout_hook_path = os.path.join(os.path.dirname(sys.executable),
                                   'python-gdb.py')
index 6fee4df..abc625d 100644 (file)
@@ -174,12 +174,10 @@ class DateTimeTests(unittest.TestCase):
             '1980-01-01 00:61:00',
             '01-01-1980 00:00:62',
             '01-01-1980T00:00:62',
-            '19800101T250000Z'
-            '1980-01-01 00:00:00 -2500',
+            '19800101T250000Z',
             ]:
             self.assertIsNone(iso2time(test),
-                              "iso2time(%s) is not None\n"
-                              "iso2time(test) %s" % (test, iso2time(test)))
+                              "iso2time(%r)" % test)
 
 
 class HeaderTests(unittest.TestCase):
index cc80f26..c916d7c 100644 (file)
@@ -19,6 +19,9 @@ import warnings
 
 from test.support import make_legacy_pyc, unload
 
+from test.test_py_compile import without_source_date_epoch
+from test.test_py_compile import SourceDateEpochTestMeta
+
 
 class SimpleTest(abc.LoaderTests):
 
@@ -359,6 +362,17 @@ class SimpleTest(abc.LoaderTests):
                     abc=importlib_abc, util=importlib_util)
 
 
+class SourceDateEpochTestMeta(SourceDateEpochTestMeta,
+                              type(Source_SimpleTest)):
+    pass
+
+
+class SourceDateEpoch_SimpleTest(Source_SimpleTest,
+                                 metaclass=SourceDateEpochTestMeta,
+                                 source_date_epoch=True):
+    pass
+
+
 class BadBytecodeTest:
 
     def import_(self, file, module_name):
@@ -617,6 +631,7 @@ class SourceLoaderBadBytecodeTest:
 
     # [bad timestamp]
     @util.writes_bytecode_files
+    @without_source_date_epoch
     def test_old_timestamp(self):
         # When the timestamp is older than the source, bytecode should be
         # regenerated.
index c68b2fe..b41141e 100644 (file)
@@ -3748,6 +3748,16 @@ class IncrementalNewlineDecoderTest(unittest.TestCase):
         dec = self.IncrementalNewlineDecoder(None, translate=True)
         _check(dec)
 
+    def test_translate(self):
+        # issue 35062
+        for translate in (-2, -1, 1, 2):
+            decoder = codecs.getincrementaldecoder("utf-8")()
+            decoder = self.IncrementalNewlineDecoder(decoder, translate)
+            self.check_newline_decoding_utf8(decoder)
+        decoder = codecs.getincrementaldecoder("utf-8")()
+        decoder = self.IncrementalNewlineDecoder(decoder, translate=0)
+        self.assertEqual(decoder.decode(b"\r\r\n"), "\r\r\n")
+
 class CIncrementalNewlineDecoderTest(IncrementalNewlineDecoderTest):
     pass
 
index 21296cc..8870c72 100644 (file)
@@ -5,12 +5,12 @@ import os
 import stat
 import sys
 import unittest
-from test.support import TESTFN, requires, unlink
+from test.support import TESTFN, requires, unlink, bigmemtest
 import io  # C implementation of io
 import _pyio as pyio # Python implementation of io
 
 # size of file to create (>2 GiB; 2 GiB == 2,147,483,648 bytes)
-size = 2500000000
+size = 2_500_000_000
 
 class LargeFileTest:
     """Test that each file function works as expected for large
@@ -45,6 +45,15 @@ class LargeFileTest:
             raise cls.failureException('File was not truncated by opening '
                                        'with mode "wb"')
 
+    # _pyio.FileIO.readall() uses a temporary bytearray then casted to bytes,
+    # so memuse=2 is needed
+    @bigmemtest(size=size, memuse=2, dry_run=False)
+    def test_large_read(self, _size):
+        # bpo-24658: Test that a read greater than 2GB does not fail.
+        with self.open(TESTFN, "rb") as f:
+            self.assertEqual(len(f.read()), size + 1)
+            self.assertEqual(f.tell(), size + 1)
+
     def test_osstat(self):
         self.assertEqual(os.stat(TESTFN)[stat.ST_SIZE], size+1)
 
index a2cc882..041a415 100644 (file)
@@ -2,7 +2,7 @@
 
 import copy
 import pickle
-from test.support import findfile
+from test import support
 import unittest
 
 import xml.dom.minidom
@@ -11,7 +11,7 @@ from xml.dom.minidom import parse, Node, Document, parseString
 from xml.dom.minidom import getDOMImplementation
 
 
-tstfile = findfile("test.xml", subdir="xmltestdata")
+tstfile = support.findfile("test.xml", subdir="xmltestdata")
 sample = ("<?xml version='1.0' encoding='us-ascii'?>\n"
           "<!DOCTYPE doc PUBLIC 'http://xml.python.org/public'"
           " 'http://xml.python.org/system' [\n"
@@ -836,6 +836,57 @@ class MinidomTest(unittest.TestCase):
     def testClonePIDeep(self):
         self.check_clone_pi(1, "testClonePIDeep")
 
+    def check_clone_node_entity(self, clone_document):
+        # bpo-35052: Test user data handler in cloneNode() on a document with
+        # an entity
+        document = xml.dom.minidom.parseString("""
+            <?xml version="1.0" ?>
+            <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+                "http://www.w3.org/TR/html4/strict.dtd"
+                [ <!ENTITY smile "☺"> ]
+            >
+            <doc>Don't let entities make you frown &smile;</doc>
+        """.strip())
+
+        class Handler:
+            def handle(self, operation, key, data, src, dst):
+                self.operation = operation
+                self.key = key
+                self.data = data
+                self.src = src
+                self.dst = dst
+
+        handler = Handler()
+        doctype = document.doctype
+        entity = doctype.entities['smile']
+        entity.setUserData("key", "data", handler)
+
+        if clone_document:
+            # clone Document
+            clone = document.cloneNode(deep=True)
+
+            self.assertEqual(clone.documentElement.firstChild.wholeText,
+                             "Don't let entities make you frown ☺")
+            operation = xml.dom.UserDataHandler.NODE_IMPORTED
+            dst = clone.doctype.entities['smile']
+        else:
+            # clone DocumentType
+            with support.swap_attr(doctype, 'ownerDocument', None):
+                clone = doctype.cloneNode(deep=True)
+
+            operation = xml.dom.UserDataHandler.NODE_CLONED
+            dst = clone.entities['smile']
+
+        self.assertEqual(handler.operation, operation)
+        self.assertEqual(handler.key, "key")
+        self.assertEqual(handler.data, "data")
+        self.assertIs(handler.src, entity)
+        self.assertIs(handler.dst, dst)
+
+    def testCloneNodeEntity(self):
+        self.check_clone_node_entity(False)
+        self.check_clone_node_entity(True)
+
     def testNormalize(self):
         doc = parseString("<doc/>")
         root = doc.documentElement
index 9f22500..5000edb 100644 (file)
@@ -1,11 +1,17 @@
 import unittest
 import test._test_multiprocessing
 
+import sys
 from test import support
 
 if support.PGO:
     raise unittest.SkipTest("test is not helpful for PGO")
 
+if sys.platform == "win32":
+    raise unittest.SkipTest("fork is not available on Windows")
+
+if sys.platform == 'darwin':
+    raise unittest.SkipTest("test may crash on macOS (bpo-33725)")
 
 test._test_multiprocessing.install_tests_in_module_dict(globals(), 'fork')
 
index 407bb3f..6ad5faf 100644 (file)
@@ -1,11 +1,15 @@
 import unittest
 import test._test_multiprocessing
 
+import sys
 from test import support
 
 if support.PGO:
     raise unittest.SkipTest("test is not helpful for PGO")
 
+if sys.platform == "win32":
+    raise unittest.SkipTest("forkserver is not available on Windows")
+
 test._test_multiprocessing.install_tests_in_module_dict(globals(), 'forkserver')
 
 if __name__ == '__main__':
index 907ca7e..6e31b64 100644 (file)
@@ -284,6 +284,8 @@ class TestNtpath(unittest.TestCase):
             tester('ntpath.abspath("")', cwd_dir)
             tester('ntpath.abspath(" ")', cwd_dir + "\\ ")
             tester('ntpath.abspath("?")', cwd_dir + "\\?")
+            drive, _ = ntpath.splitdrive(cwd_dir)
+            tester('ntpath.abspath("/abc/")', drive + "\\abc")
 
     def test_relpath(self):
         tester('ntpath.relpath("a")', 'a')
index 20efe37..b1d7f86 100644 (file)
@@ -697,9 +697,9 @@ class CPythonOrderedDictTests(OrderedDictTests, unittest.TestCase):
         nodesize = calcsize('Pn2P')
 
         od = OrderedDict()
-        check(od, basicsize + 8*p + 8 + 5*entrysize)  # 8byte indices + 8*2//3 * entry table
+        check(od, basicsize + 8 + 5*entrysize)  # 8byte indices + 8*2//3 * entry table
         od.x = 1
-        check(od, basicsize + 8*p + 8 + 5*entrysize)
+        check(od, basicsize + 8 + 5*entrysize)
         od.update([(i, i) for i in range(3)])
         check(od, basicsize + 8*p + 8 + 5*entrysize + 3*nodesize)
         od.update([(i, i) for i in range(3, 10)])
@@ -732,6 +732,23 @@ class CPythonOrderedDictTests(OrderedDictTests, unittest.TestCase):
                 del od['c']
         self.assertEqual(list(od), list('bdeaf'))
 
+    def test_iterators_pickling(self):
+        OrderedDict = self.OrderedDict
+        pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
+        od = OrderedDict(pairs)
+
+        for method_name in ('keys', 'values', 'items'):
+            meth = getattr(od, method_name)
+            expected = list(meth())[1:]
+            for i in range(pickle.HIGHEST_PROTOCOL + 1):
+                with self.subTest(method_name=method_name, protocol=i):
+                    it = iter(meth())
+                    next(it)
+                    p = pickle.dumps(it, i)
+                    unpickled = pickle.loads(p)
+                    self.assertEqual(list(unpickled), expected)
+                    self.assertEqual(list(it), expected)
+
 
 class PurePythonOrderedDictSubclassTests(PurePythonOrderedDictTests):
 
index cef4a09..fe7261d 100644 (file)
@@ -634,6 +634,29 @@ class UtimeTests(unittest.TestCase):
         # seconds and nanoseconds parameters are mutually exclusive
         with self.assertRaises(ValueError):
             os.utime(self.fname, (5, 5), ns=(5, 5))
+        with self.assertRaises(TypeError):
+            os.utime(self.fname, [5, 5])
+        with self.assertRaises(TypeError):
+            os.utime(self.fname, (5,))
+        with self.assertRaises(TypeError):
+            os.utime(self.fname, (5, 5, 5))
+        with self.assertRaises(TypeError):
+            os.utime(self.fname, ns=[5, 5])
+        with self.assertRaises(TypeError):
+            os.utime(self.fname, ns=(5,))
+        with self.assertRaises(TypeError):
+            os.utime(self.fname, ns=(5, 5, 5))
+
+        if os.utime not in os.supports_follow_symlinks:
+            with self.assertRaises(NotImplementedError):
+                os.utime(self.fname, (5, 5), follow_symlinks=False)
+        if os.utime not in os.supports_fd:
+            with open(self.fname, 'wb', 0) as fp:
+                with self.assertRaises(TypeError):
+                    os.utime(fp.fileno(), (5, 5))
+        if os.utime not in os.supports_dir_fd:
+            with self.assertRaises(NotImplementedError):
+                os.utime(self.fname, (5, 5), dir_fd=0)
 
     @support.cpython_only
     def test_issue31577(self):
@@ -1589,6 +1612,16 @@ class ExecTests(unittest.TestCase):
         with self.assertRaises(ValueError):
             os.execve(args[0], args, newenv)
 
+    @unittest.skipUnless(sys.platform == "win32", "Win32-specific test")
+    def test_execve_with_empty_path(self):
+        # bpo-32890: Check GetLastError() misuse
+        try:
+            os.execve('', ['arg'], {})
+        except OSError as e:
+            self.assertTrue(e.winerror is None or e.winerror != 0)
+        else:
+            self.fail('No OSError raised')
+
 
 @unittest.skipUnless(sys.platform == "win32", "Win32 specific tests")
 class Win32ErrorTests(unittest.TestCase):
index e436db9..056507e 100644 (file)
@@ -1519,7 +1519,7 @@ class _BasePathTest(object):
             # resolves to 'dirB/..' first before resolving to parent of dirB.
             self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False)
         # Now create absolute symlinks
-        d = support._longpath(tempfile.mkdtemp(suffix='-dirD'))
+        d = support._longpath(tempfile.mkdtemp(suffix='-dirD', dir=os.getcwd()))
         self.addCleanup(support.rmtree, d)
         os.symlink(os.path.join(d), join('dirA', 'linkX'))
         os.symlink(join('dirB'), os.path.join(d, 'linkY'))
index 9476ede..e73b31c 100644 (file)
@@ -5,6 +5,7 @@ import warnings
 from posixpath import realpath, abspath, dirname, basename
 from test import support, test_genericpath
 from test.support import FakePath
+from unittest import mock
 
 try:
     import posix
@@ -230,42 +231,61 @@ class PosixPathTest(unittest.TestCase):
     def test_expanduser(self):
         self.assertEqual(posixpath.expanduser("foo"), "foo")
         self.assertEqual(posixpath.expanduser(b"foo"), b"foo")
+
+    def test_expanduser_home_envvar(self):
         with support.EnvironmentVarGuard() as env:
+            env['HOME'] = '/home/victor'
+            self.assertEqual(posixpath.expanduser("~"), "/home/victor")
+
+            # expanduser() strips trailing slash
+            env['HOME'] = '/home/victor/'
+            self.assertEqual(posixpath.expanduser("~"), "/home/victor")
+
             for home in '/', '', '//', '///':
                 with self.subTest(home=home):
                     env['HOME'] = home
                     self.assertEqual(posixpath.expanduser("~"), "/")
                     self.assertEqual(posixpath.expanduser("~/"), "/")
                     self.assertEqual(posixpath.expanduser("~/foo"), "/foo")
-        try:
-            import pwd
-        except ImportError:
-            pass
-        else:
-            self.assertIsInstance(posixpath.expanduser("~/"), str)
-            self.assertIsInstance(posixpath.expanduser(b"~/"), bytes)
-            # if home directory == root directory, this test makes no sense
-            if posixpath.expanduser("~") != '/':
-                self.assertEqual(
-                    posixpath.expanduser("~") + "/",
-                    posixpath.expanduser("~/")
-                )
-                self.assertEqual(
-                    posixpath.expanduser(b"~") + b"/",
-                    posixpath.expanduser(b"~/")
-                )
-            self.assertIsInstance(posixpath.expanduser("~root/"), str)
-            self.assertIsInstance(posixpath.expanduser("~foo/"), str)
-            self.assertIsInstance(posixpath.expanduser(b"~root/"), bytes)
-            self.assertIsInstance(posixpath.expanduser(b"~foo/"), bytes)
-
-            with support.EnvironmentVarGuard() as env:
-                # expanduser should fall back to using the password database
-                del env['HOME']
-                home = pwd.getpwuid(os.getuid()).pw_dir
-                # $HOME can end with a trailing /, so strip it (see #17809)
-                home = home.rstrip("/") or '/'
-                self.assertEqual(posixpath.expanduser("~"), home)
+
+    def test_expanduser_pwd(self):
+        pwd = support.import_module('pwd')
+
+        self.assertIsInstance(posixpath.expanduser("~/"), str)
+        self.assertIsInstance(posixpath.expanduser(b"~/"), bytes)
+
+        # if home directory == root directory, this test makes no sense
+        if posixpath.expanduser("~") != '/':
+            self.assertEqual(
+                posixpath.expanduser("~") + "/",
+                posixpath.expanduser("~/")
+            )
+            self.assertEqual(
+                posixpath.expanduser(b"~") + b"/",
+                posixpath.expanduser(b"~/")
+            )
+        self.assertIsInstance(posixpath.expanduser("~root/"), str)
+        self.assertIsInstance(posixpath.expanduser("~foo/"), str)
+        self.assertIsInstance(posixpath.expanduser(b"~root/"), bytes)
+        self.assertIsInstance(posixpath.expanduser(b"~foo/"), bytes)
+
+        with support.EnvironmentVarGuard() as env:
+            # expanduser should fall back to using the password database
+            del env['HOME']
+
+            home = pwd.getpwuid(os.getuid()).pw_dir
+            # $HOME can end with a trailing /, so strip it (see #17809)
+            home = home.rstrip("/") or '/'
+            self.assertEqual(posixpath.expanduser("~"), home)
+
+            # bpo-10496: If the HOME environment variable is not set and the
+            # user (current identifier or name in the path) doesn't exist in
+            # the password database (pwd.getuid() or pwd.getpwnam() fail),
+            # expanduser() must return the path unchanged.
+            with mock.patch.object(pwd, 'getpwuid', side_effect=KeyError), \
+                 mock.patch.object(pwd, 'getpwnam', side_effect=KeyError):
+                for path in ('~', '~/.local', '~vstinner/'):
+                    self.assertEqual(posixpath.expanduser(path), path)
 
     def test_normpath(self):
         self.assertEqual(posixpath.normpath(""), ".")
index 8fc0b33..f86abe2 100644 (file)
@@ -1,3 +1,4 @@
+import functools
 import importlib.util
 import os
 import py_compile
@@ -10,7 +11,44 @@ import unittest
 from test import support
 
 
-class PyCompileTests(unittest.TestCase):
+def without_source_date_epoch(fxn):
+    """Runs function with SOURCE_DATE_EPOCH unset."""
+    @functools.wraps(fxn)
+    def wrapper(*args, **kwargs):
+        with support.EnvironmentVarGuard() as env:
+            env.unset('SOURCE_DATE_EPOCH')
+            return fxn(*args, **kwargs)
+    return wrapper
+
+
+def with_source_date_epoch(fxn):
+    """Runs function with SOURCE_DATE_EPOCH set."""
+    @functools.wraps(fxn)
+    def wrapper(*args, **kwargs):
+        with support.EnvironmentVarGuard() as env:
+            env['SOURCE_DATE_EPOCH'] = '123456789'
+            return fxn(*args, **kwargs)
+    return wrapper
+
+
+# Run tests with SOURCE_DATE_EPOCH set or unset explicitly.
+class SourceDateEpochTestMeta(type(unittest.TestCase)):
+    def __new__(mcls, name, bases, dct, *, source_date_epoch):
+        cls = super().__new__(mcls, name, bases, dct)
+
+        for attr in dir(cls):
+            if attr.startswith('test_'):
+                meth = getattr(cls, attr)
+                if source_date_epoch:
+                    wrapper = with_source_date_epoch(meth)
+                else:
+                    wrapper = without_source_date_epoch(meth)
+                setattr(cls, attr, wrapper)
+
+        return cls
+
+
+class PyCompileTestsBase:
 
     def setUp(self):
         self.directory = tempfile.mkdtemp()
@@ -99,16 +137,18 @@ class PyCompileTests(unittest.TestCase):
             importlib.util.cache_from_source(bad_coding)))
 
     def test_source_date_epoch(self):
-        testtime = 123456789
-        with support.EnvironmentVarGuard() as env:
-            env["SOURCE_DATE_EPOCH"] = str(testtime)
-            py_compile.compile(self.source_path, self.pyc_path)
+        py_compile.compile(self.source_path, self.pyc_path)
         self.assertTrue(os.path.exists(self.pyc_path))
         self.assertFalse(os.path.exists(self.cache_path))
         with open(self.pyc_path, 'rb') as fp:
             flags = importlib._bootstrap_external._classify_pyc(
                 fp.read(), 'test', {})
-        self.assertEqual(flags, 0b11)
+        if os.environ.get('SOURCE_DATE_EPOCH'):
+            expected_flags = 0b11
+        else:
+            expected_flags = 0b00
+
+        self.assertEqual(flags, expected_flags)
 
     @unittest.skipIf(sys.flags.optimize > 0, 'test does not work with -O')
     def test_double_dot_no_clobber(self):
@@ -153,5 +193,19 @@ class PyCompileTests(unittest.TestCase):
         self.assertEqual(flags, 0b1)
 
 
+class PyCompileTestsWithSourceEpoch(PyCompileTestsBase,
+                                    unittest.TestCase,
+                                    metaclass=SourceDateEpochTestMeta,
+                                    source_date_epoch=True):
+    pass
+
+
+class PyCompileTestsWithoutSourceEpoch(PyCompileTestsBase,
+                                       unittest.TestCase,
+                                       metaclass=SourceDateEpochTestMeta,
+                                       source_date_epoch=False):
+    pass
+
+
 if __name__ == "__main__":
     unittest.main()
index 06f8729..9bf365e 100644 (file)
@@ -417,6 +417,7 @@ class PydocBaseTest(unittest.TestCase):
 
 
 class PydocDocTest(unittest.TestCase):
+    maxDiff = None
 
     @unittest.skipIf(sys.flags.optimize >= 2,
                      "Docstrings are omitted with -O2 and above")
@@ -648,6 +649,104 @@ class PydocDocTest(unittest.TestCase):
         methods = pydoc.allmethods(TestClass)
         self.assertDictEqual(methods, expected)
 
+    def test_method_aliases(self):
+        class A:
+            def tkraise(self, aboveThis=None):
+                """Raise this widget in the stacking order."""
+            lift = tkraise
+            def a_size(self):
+                """Return size"""
+        class B(A):
+            def itemconfigure(self, tagOrId, cnf=None, **kw):
+                """Configure resources of an item TAGORID."""
+            itemconfig = itemconfigure
+            b_size = A.a_size
+
+        doc = pydoc.render_doc(B)
+        # clean up the extra text formatting that pydoc performs
+        doc = re.sub('\b.', '', doc)
+        self.assertEqual(doc, '''\
+Python Library Documentation: class B in module %s
+
+class B(A)
+ |  Method resolution order:
+ |      B
+ |      A
+ |      builtins.object
+ |\x20\x20
+ |  Methods defined here:
+ |\x20\x20
+ |  b_size = a_size(self)
+ |\x20\x20
+ |  itemconfig = itemconfigure(self, tagOrId, cnf=None, **kw)
+ |\x20\x20
+ |  itemconfigure(self, tagOrId, cnf=None, **kw)
+ |      Configure resources of an item TAGORID.
+ |\x20\x20
+ |  ----------------------------------------------------------------------
+ |  Methods inherited from A:
+ |\x20\x20
+ |  a_size(self)
+ |      Return size
+ |\x20\x20
+ |  lift = tkraise(self, aboveThis=None)
+ |\x20\x20
+ |  tkraise(self, aboveThis=None)
+ |      Raise this widget in the stacking order.
+ |\x20\x20
+ |  ----------------------------------------------------------------------
+ |  Data descriptors inherited from A:
+ |\x20\x20
+ |  __dict__
+ |      dictionary for instance variables (if defined)
+ |\x20\x20
+ |  __weakref__
+ |      list of weak references to the object (if defined)
+''' % __name__)
+
+        doc = pydoc.render_doc(B, renderer=pydoc.HTMLDoc())
+        self.assertEqual(doc, '''\
+Python Library Documentation: class B in module %s
+
+<p>
+<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="B">class <strong>B</strong></a>(A)</font></td></tr>
+\x20\x20\x20\x20
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%%"><dl><dt>Method resolution order:</dt>
+<dd>B</dd>
+<dd>A</dd>
+<dd><a href="builtins.html#object">builtins.object</a></dd>
+</dl>
+<hr>
+Methods defined here:<br>
+<dl><dt><a name="B-b_size"><strong>b_size</strong></a> = <a href="#B-a_size">a_size</a>(self)</dt></dl>
+
+<dl><dt><a name="B-itemconfig"><strong>itemconfig</strong></a> = <a href="#B-itemconfigure">itemconfigure</a>(self, tagOrId, cnf=None, **kw)</dt></dl>
+
+<dl><dt><a name="B-itemconfigure"><strong>itemconfigure</strong></a>(self, tagOrId, cnf=None, **kw)</dt><dd><tt>Configure&nbsp;resources&nbsp;of&nbsp;an&nbsp;item&nbsp;TAGORID.</tt></dd></dl>
+
+<hr>
+Methods inherited from A:<br>
+<dl><dt><a name="B-a_size"><strong>a_size</strong></a>(self)</dt><dd><tt>Return&nbsp;size</tt></dd></dl>
+
+<dl><dt><a name="B-lift"><strong>lift</strong></a> = <a href="#B-tkraise">tkraise</a>(self, aboveThis=None)</dt></dl>
+
+<dl><dt><a name="B-tkraise"><strong>tkraise</strong></a>(self, aboveThis=None)</dt><dd><tt>Raise&nbsp;this&nbsp;widget&nbsp;in&nbsp;the&nbsp;stacking&nbsp;order.</tt></dd></dl>
+
+<hr>
+Data descriptors inherited from A:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table>\
+''' % __name__)
+
 
 class PydocImportTest(PydocBaseTest):
 
index ce15847..94c96a9 100644 (file)
@@ -39,7 +39,7 @@ class RangeTest(unittest.TestCase):
                 self.fail('{}: unexpected excess element {} at '
                           'position {}'.format(test_id, x, i))
             else:
-                self.fail('{}: wrong element at position {};'
+                self.fail('{}: wrong element at position {}; '
                           'expected {}, got {}'.format(test_id, i, y, x))
 
     def test_range(self):
index af332ad..db9bd6d 100644 (file)
@@ -15,7 +15,6 @@ import sys
 import sysconfig
 import tempfile
 import textwrap
-import threading
 import unittest
 from test import libregrtest
 from test import support
@@ -67,10 +66,10 @@ class ParseArgsTestCase(unittest.TestCase):
         ns = libregrtest._parse_args(['--wait'])
         self.assertTrue(ns.wait)
 
-    def test_slaveargs(self):
-        ns = libregrtest._parse_args(['--slaveargs', '[[], {}]'])
-        self.assertEqual(ns.slaveargs, '[[], {}]')
-        self.checkError(['--slaveargs'], 'expected one argument')
+    def test_worker_args(self):
+        ns = libregrtest._parse_args(['--worker-args', '[[], {}]'])
+        self.assertEqual(ns.worker_args, '[[], {}]')
+        self.checkError(['--worker-args'], 'expected one argument')
 
     def test_start(self):
         for opt in '-S', '--start':
@@ -352,11 +351,20 @@ class BaseTestCase(unittest.TestCase):
         self.tmptestdir = tempfile.mkdtemp()
         self.addCleanup(support.rmtree, self.tmptestdir)
 
-    def create_test(self, name=None, code=''):
+    def create_test(self, name=None, code=None):
         if not name:
             name = 'noop%s' % BaseTestCase.TEST_UNIQUE_ID
             BaseTestCase.TEST_UNIQUE_ID += 1
 
+        if code is None:
+            code = textwrap.dedent("""
+                    import unittest
+
+                    class Tests(unittest.TestCase):
+                        def test_empty_test(self):
+                            pass
+                """)
+
         # test_regrtest cannot be run twice in parallel because
         # of setUp() and create_test()
         name = self.TESTNAME_PREFIX + name
@@ -391,7 +399,7 @@ class BaseTestCase(unittest.TestCase):
 
     def check_executed_tests(self, output, tests, skipped=(), failed=(),
                              env_changed=(), omitted=(),
-                             rerun=(),
+                             rerun=(), no_test_ran=(),
                              randomize=False, interrupted=False,
                              fail_env_changed=False):
         if isinstance(tests, str):
@@ -406,6 +414,8 @@ class BaseTestCase(unittest.TestCase):
             omitted = [omitted]
         if isinstance(rerun, str):
             rerun = [rerun]
+        if isinstance(no_test_ran, str):
+            no_test_ran = [no_test_ran]
 
         executed = self.parse_executed_tests(output)
         if randomize:
@@ -448,8 +458,12 @@ class BaseTestCase(unittest.TestCase):
                 regex = "Re-running test %r in verbose mode" % name
                 self.check_line(output, regex)
 
+        if no_test_ran:
+            regex = list_regex('%s test%s run no tests', no_test_ran)
+            self.check_line(output, regex)
+
         good = (len(tests) - len(skipped) - len(failed)
-                - len(omitted) - len(env_changed))
+                - len(omitted) - len(env_changed) - len(no_test_ran))
         if good:
             regex = r'%s test%s OK\.$' % (good, plural(good))
             if not skipped and not failed and good > 1:
@@ -466,12 +480,16 @@ class BaseTestCase(unittest.TestCase):
             result.append('ENV CHANGED')
         if interrupted:
             result.append('INTERRUPTED')
-        if not result:
+        if not any((good, result, failed, interrupted, skipped,
+                    env_changed, fail_env_changed)):
+            result.append("NO TEST RUN")
+        elif not result:
             result.append('SUCCESS')
         result = ', '.join(result)
         if rerun:
             self.check_line(output, 'Tests result: %s' % result)
             result = 'FAILURE then %s' % result
+
         self.check_line(output, 'Tests result: %s' % result)
 
     def parse_random_seed(self, output):
@@ -650,7 +668,14 @@ class ArgsTestCase(BaseTestCase):
         # test -u command line option
         tests = {}
         for resource in ('audio', 'network'):
-            code = 'from test import support\nsupport.requires(%r)' % resource
+            code = textwrap.dedent("""
+                        from test import support; support.requires(%r)
+                        import unittest
+                        class PassingTest(unittest.TestCase):
+                            def test_pass(self):
+                                pass
+                    """ % resource)
+
             tests[resource] = self.create_test(resource, code)
         test_names = sorted(tests.values())
 
@@ -979,6 +1004,56 @@ class ArgsTestCase(BaseTestCase):
         output = self.run_tests("-w", testname, exitcode=2)
         self.check_executed_tests(output, [testname],
                                   failed=testname, rerun=testname)
+    def test_no_tests_ran(self):
+        code = textwrap.dedent("""
+            import unittest
+
+            class Tests(unittest.TestCase):
+                def test_bug(self):
+                    pass
+        """)
+        testname = self.create_test(code=code)
+
+        output = self.run_tests(testname, "-m", "nosuchtest", exitcode=0)
+        self.check_executed_tests(output, [testname], no_test_ran=testname)
+
+    def test_no_tests_ran_multiple_tests_nonexistent(self):
+        code = textwrap.dedent("""
+            import unittest
+
+            class Tests(unittest.TestCase):
+                def test_bug(self):
+                    pass
+        """)
+        testname = self.create_test(code=code)
+        testname2 = self.create_test(code=code)
+
+        output = self.run_tests(testname, testname2, "-m", "nosuchtest", exitcode=0)
+        self.check_executed_tests(output, [testname, testname2],
+                                  no_test_ran=[testname, testname2])
+
+    def test_no_test_ran_some_test_exist_some_not(self):
+        code = textwrap.dedent("""
+            import unittest
+
+            class Tests(unittest.TestCase):
+                def test_bug(self):
+                    pass
+        """)
+        testname = self.create_test(code=code)
+        other_code = textwrap.dedent("""
+            import unittest
+
+            class Tests(unittest.TestCase):
+                def test_other_bug(self):
+                    pass
+        """)
+        testname2 = self.create_test(code=other_code)
+
+        output = self.run_tests(testname, testname2, "-m", "nosuchtest",
+                                "-m", "test_other_bug", exitcode=0)
+        self.check_executed_tests(output, [testname, testname2],
+                                  no_test_ran=[testname])
 
 
 class TestUtils(unittest.TestCase):
index 6cea58d..a30bd2f 100644 (file)
@@ -6,6 +6,7 @@ executing have not been removed.
 """
 import unittest
 import test.support
+from test import support
 from test.support import (captured_stderr, TESTFN, EnvironmentVarGuard,
                           change_cwd)
 import builtins
@@ -19,6 +20,7 @@ import shutil
 import subprocess
 import sysconfig
 import tempfile
+from unittest import mock
 from copy import copy
 
 # These tests are not particularly useful if Python was invoked with -S.
@@ -258,6 +260,7 @@ class HelperFunctionsTests(unittest.TestCase):
         # the call sets USER_BASE *and* USER_SITE
         self.assertEqual(site.USER_SITE, user_site)
         self.assertTrue(user_site.startswith(site.USER_BASE), user_site)
+        self.assertEqual(site.USER_BASE, site.getuserbase())
 
     def test_getsitepackages(self):
         site.PREFIXES = ['xoxo']
@@ -276,6 +279,40 @@ class HelperFunctionsTests(unittest.TestCase):
             wanted = os.path.join('xoxo', 'lib', 'site-packages')
             self.assertEqual(dirs[1], wanted)
 
+    def test_no_home_directory(self):
+        # bpo-10496: getuserbase() and getusersitepackages() must not fail if
+        # the current user has no home directory (if expanduser() returns the
+        # path unchanged).
+        site.USER_SITE = None
+        site.USER_BASE = None
+
+        with EnvironmentVarGuard() as environ, \
+             mock.patch('os.path.expanduser', lambda path: path):
+
+            del environ['PYTHONUSERBASE']
+            del environ['APPDATA']
+
+            user_base = site.getuserbase()
+            self.assertTrue(user_base.startswith('~' + os.sep),
+                            user_base)
+
+            user_site = site.getusersitepackages()
+            self.assertTrue(user_site.startswith(user_base), user_site)
+
+        with mock.patch('os.path.isdir', return_value=False) as mock_isdir, \
+             mock.patch.object(site, 'addsitedir') as mock_addsitedir, \
+             support.swap_attr(site, 'ENABLE_USER_SITE', True):
+
+            # addusersitepackages() must not add user_site to sys.path
+            # if it is not an existing directory
+            known_paths = set()
+            site.addusersitepackages(known_paths)
+
+            mock_isdir.assert_called_once_with(user_site)
+            mock_addsitedir.assert_not_called()
+            self.assertFalse(known_paths)
+
+
 class PthFile(object):
     """Helper class for handling testing of .pth files"""
 
index 8a29e98..b4149d3 100644 (file)
@@ -770,7 +770,7 @@ class SimSMTPChannel(smtpd.SMTPChannel):
             try:
                 user, hashed_pass = logpass.split()
             except ValueError as e:
-                self.push('535 Splitting response {!r} into user and password'
+                self.push('535 Splitting response {!r} into user and password '
                           'failed: {}'.format(logpass, e))
                 return False
             valid_hashed_pass = hmac.HMAC(
index 5f9891d..9b9d113 100644 (file)
@@ -35,6 +35,7 @@ except ImportError:
 
 HOST = support.HOST
 MSG = 'Michael Gilfix was here\u1234\r\n'.encode('utf-8') ## test unicode string and carriage return
+MAIN_TIMEOUT = 60.0
 
 VSOCKPORT = 1234
 
@@ -4174,6 +4175,7 @@ class BasicSocketPairTest(SocketPairTest):
 class NonBlockingTCPTests(ThreadedTCPSocketTest):
 
     def __init__(self, methodName='runTest'):
+        self.event = threading.Event()
         ThreadedTCPSocketTest.__init__(self, methodName=methodName)
 
     def testSetBlocking(self):
@@ -4286,22 +4288,27 @@ class NonBlockingTCPTests(ThreadedTCPSocketTest):
     def testAccept(self):
         # Testing non-blocking accept
         self.serv.setblocking(0)
-        try:
-            conn, addr = self.serv.accept()
-        except OSError:
-            pass
-        else:
-            self.fail("Error trying to do non-blocking accept.")
-        read, write, err = select.select([self.serv], [], [])
-        if self.serv in read:
+
+        # connect() didn't start: non-blocking accept() fails
+        with self.assertRaises(BlockingIOError):
             conn, addr = self.serv.accept()
-            self.assertIsNone(conn.gettimeout())
-            conn.close()
-        else:
+
+        self.event.set()
+
+        read, write, err = select.select([self.serv], [], [], MAIN_TIMEOUT)
+        if self.serv not in read:
             self.fail("Error trying to do accept after select.")
 
+        # connect() completed: non-blocking accept() doesn't block
+        conn, addr = self.serv.accept()
+        self.addCleanup(conn.close)
+        self.assertIsNone(conn.gettimeout())
+
     def _testAccept(self):
-        time.sleep(0.1)
+        # don't connect before event is set to check
+        # that non-blocking accept() raises BlockingIOError
+        self.event.wait()
+
         self.cli.connect((HOST, self.port))
 
     def testConnect(self):
@@ -4316,25 +4323,32 @@ class NonBlockingTCPTests(ThreadedTCPSocketTest):
     def testRecv(self):
         # Testing non-blocking recv
         conn, addr = self.serv.accept()
+        self.addCleanup(conn.close)
         conn.setblocking(0)
-        try:
-            msg = conn.recv(len(MSG))
-        except OSError:
-            pass
-        else:
-            self.fail("Error trying to do non-blocking recv.")
-        read, write, err = select.select([conn], [], [])
-        if conn in read:
+
+        # the server didn't send data yet: non-blocking recv() fails
+        with self.assertRaises(BlockingIOError):
             msg = conn.recv(len(MSG))
-            conn.close()
-            self.assertEqual(msg, MSG)
-        else:
+
+        self.event.set()
+
+        read, write, err = select.select([conn], [], [], MAIN_TIMEOUT)
+        if conn not in read:
             self.fail("Error during select call to non-blocking socket.")
 
+        # the server sent data yet: non-blocking recv() doesn't block
+        msg = conn.recv(len(MSG))
+        self.assertEqual(msg, MSG)
+
     def _testRecv(self):
         self.cli.connect((HOST, self.port))
-        time.sleep(0.1)
-        self.cli.send(MSG)
+
+        # don't send anything before event is set to check
+        # that non-blocking recv() raises BlockingIOError
+        self.event.wait()
+
+        # send data: recv() will no longer block
+        self.cli.sendall(MSG)
 
 
 class FileObjectClassTestCase(SocketConnectedTest):
@@ -5930,6 +5944,24 @@ class LinuxKernelCryptoAPI(unittest.TestCase):
             with self.assertRaises(TypeError):
                 sock.sendmsg_afalg(op=socket.ALG_OP_ENCRYPT, assoclen=-1)
 
+    def test_length_restriction(self):
+        # bpo-35050, off-by-one error in length check
+        sock = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0)
+        self.addCleanup(sock.close)
+
+        # salg_type[14]
+        with self.assertRaises(FileNotFoundError):
+            sock.bind(("t" * 13, "name"))
+        with self.assertRaisesRegex(ValueError, "type too long"):
+            sock.bind(("t" * 14, "name"))
+
+        # salg_name[64]
+        with self.assertRaises(FileNotFoundError):
+            sock.bind(("type", "n" * 63))
+        with self.assertRaisesRegex(ValueError, "name too long"):
+            sock.bind(("type", "n" * 64))
+
+
 @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
 class TestMSWindowsTCPFlags(unittest.TestCase):
     knownTCPFlags = {
index d4132c5..f1b9565 100644 (file)
@@ -480,8 +480,12 @@ class BasicSocketTests(unittest.TestCase):
             self.assertRaises(OSError, ss.recvfrom_into, bytearray(b'x'), 1)
             self.assertRaises(OSError, ss.send, b'x')
             self.assertRaises(OSError, ss.sendto, b'x', ('0.0.0.0', 0))
+            self.assertRaises(NotImplementedError, ss.dup)
             self.assertRaises(NotImplementedError, ss.sendmsg,
                               [b'x'], (), 0, ('0.0.0.0', 0))
+            self.assertRaises(NotImplementedError, ss.recvmsg, 100)
+            self.assertRaises(NotImplementedError, ss.recvmsg_into,
+                              [bytearray(100)])
 
     def test_timeout(self):
         # Issue #8524: when creating an SSL socket, the timeout of the
@@ -3410,10 +3414,11 @@ class ThreadedTests(unittest.TestCase):
             # Make sure sendmsg et al are disallowed to avoid
             # inadvertent disclosure of data and/or corruption
             # of the encrypted data stream
+            self.assertRaises(NotImplementedError, s.dup)
             self.assertRaises(NotImplementedError, s.sendmsg, [b"data"])
             self.assertRaises(NotImplementedError, s.recvmsg, 100)
             self.assertRaises(NotImplementedError,
-                              s.recvmsg_into, bytearray(100))
+                              s.recvmsg_into, [bytearray(100)])
             s.write(b"over\n")
 
             self.assertRaises(ValueError, s.recv, -1)
index de2773f..623da40 100644 (file)
@@ -506,7 +506,7 @@ class CalculationTests(unittest.TestCase):
         self.assertTrue(result.tm_year == self.time_tuple.tm_year and
                          result.tm_mon == self.time_tuple.tm_mon and
                          result.tm_mday == self.time_tuple.tm_mday,
-                        "Calculation of Gregorian date failed;"
+                        "Calculation of Gregorian date failed; "
                          "%s-%s-%s != %s-%s-%s" %
                          (result.tm_year, result.tm_mon, result.tm_mday,
                           self.time_tuple.tm_year, self.time_tuple.tm_mon,
@@ -518,7 +518,7 @@ class CalculationTests(unittest.TestCase):
         result = _strptime._strptime_time(time.strftime(format_string, self.time_tuple),
                                     format_string)
         self.assertTrue(result.tm_wday == self.time_tuple.tm_wday,
-                        "Calculation of day of the week failed;"
+                        "Calculation of day of the week failed; "
                          "%s != %s" % (result.tm_wday, self.time_tuple.tm_wday))
 
     if support.is_android:
@@ -692,7 +692,7 @@ class CacheTests(unittest.TestCase):
         finally:
             locale.setlocale(locale.LC_TIME, locale_info)
 
-    @support.run_with_tz('STD-1DST')
+    @support.run_with_tz('STD-1DST,M4.1.0,M10.1.0')
     def test_TimeRE_recreation_timezone(self):
         # The TimeRE instance should be recreated upon changing the timezone.
         oldtzname = time.tzname
index 7870e94..e29846e 100644 (file)
@@ -456,7 +456,7 @@ class TestSupport(unittest.TestCase):
         # pending child process
         support.reap_children()
 
-    def check_options(self, args, func):
+    def check_options(self, args, func, expected=None):
         code = f'from test.support import {func}; print(repr({func}()))'
         cmd = [sys.executable, *args, '-c', code]
         env = {key: value for key, value in os.environ.items()
@@ -466,7 +466,9 @@ class TestSupport(unittest.TestCase):
                               stderr=subprocess.DEVNULL,
                               universal_newlines=True,
                               env=env)
-        self.assertEqual(proc.stdout.rstrip(), repr(args))
+        if expected is None:
+            expected = args
+        self.assertEqual(proc.stdout.rstrip(), repr(expected))
         self.assertEqual(proc.returncode, 0)
 
     def test_args_from_interpreter_flags(self):
@@ -482,6 +484,7 @@ class TestSupport(unittest.TestCase):
             ['-v'],
             ['-b'],
             ['-q'],
+            ['-I'],
             # same option multiple times
             ['-bb'],
             ['-vvv'],
@@ -500,6 +503,9 @@ class TestSupport(unittest.TestCase):
             with self.subTest(opts=opts):
                 self.check_options(opts, 'args_from_interpreter_flags')
 
+        self.check_options(['-I', '-E', '-s'], 'args_from_interpreter_flags',
+                           ['-I'])
+
     def test_optim_args_from_interpreter_flags(self):
         # Test test.support.optim_args_from_interpreter_flags()
         for opts in (
index 62abd89..ea455c0 100644 (file)
@@ -47,12 +47,6 @@ ROUNDING_MODES = (
 )
 
 
-def busy_wait(duration):
-    deadline = time.monotonic() + duration
-    while time.monotonic() < deadline:
-        pass
-
-
 class TimeTestCase(unittest.TestCase):
 
     def setUp(self):
@@ -496,24 +490,6 @@ class TimeTestCase(unittest.TestCase):
         # on Windows
         self.assertLess(stop - start, 0.020)
 
-        # bpo-33723: A busy loop of 100 ms should increase process_time()
-        # by at least 15 ms
-        min_time = 0.015
-        busy_time = 0.100
-
-        # process_time() should include CPU time spent in any thread
-        start = time.process_time()
-        busy_wait(busy_time)
-        stop = time.process_time()
-        self.assertGreaterEqual(stop - start, min_time)
-
-        t = threading.Thread(target=busy_wait, args=(busy_time,))
-        start = time.process_time()
-        t.start()
-        t.join()
-        stop = time.process_time()
-        self.assertGreaterEqual(stop - start, min_time)
-
         info = time.get_clock_info('process_time')
         self.assertTrue(info.monotonic)
         self.assertFalse(info.adjustable)
@@ -534,25 +510,6 @@ class TimeTestCase(unittest.TestCase):
         # on Windows
         self.assertLess(stop - start, 0.020)
 
-        # bpo-33723: A busy loop of 100 ms should increase thread_time()
-        # by at least 15 ms
-        min_time = 0.015
-        busy_time = 0.100
-
-        # thread_time() should include CPU time spent in current thread...
-        start = time.thread_time()
-        busy_wait(busy_time)
-        stop = time.thread_time()
-        self.assertGreaterEqual(stop - start, min_time)
-
-        # ...but not in other threads
-        t = threading.Thread(target=busy_wait, args=(busy_time,))
-        start = time.thread_time()
-        t.start()
-        t.join()
-        stop = time.thread_time()
-        self.assertLess(stop - start, min_time)
-
         info = time.get_clock_info('thread_time')
         self.assertTrue(info.monotonic)
         self.assertFalse(info.adjustable)
index 6d8cc53..0d66ebb 100644 (file)
@@ -2075,6 +2075,22 @@ class CollectionsAbcTests(BaseTestCase):
         self.assertIsSubclass(MyDefDict, collections.defaultdict)
         self.assertNotIsSubclass(collections.defaultdict, MyDefDict)
 
+    def test_ordereddict_instantiation(self):
+        self.assertIs(type(typing.OrderedDict()), collections.OrderedDict)
+        self.assertIs(type(typing.OrderedDict[KT, VT]()), collections.OrderedDict)
+        self.assertIs(type(typing.OrderedDict[str, int]()), collections.OrderedDict)
+
+    def test_ordereddict_subclass(self):
+
+        class MyOrdDict(typing.OrderedDict[str, int]):
+            pass
+
+        od = MyOrdDict()
+        self.assertIsInstance(od, MyOrdDict)
+
+        self.assertIsSubclass(MyOrdDict, collections.OrderedDict)
+        self.assertNotIsSubclass(collections.OrderedDict, MyOrdDict)
+
     @skipUnless(sys.version_info >= (3, 3), 'ChainMap was added in 3.3')
     def test_chainmap_instantiation(self):
         self.assertIs(type(typing.ChainMap()), collections.ChainMap)
index 15f73de..1aa64cb 100644 (file)
@@ -27,6 +27,13 @@ def _wrap_with_retry_thrice(func, exc):
         return _retry_thrice(func, exc, *args, **kwargs)
     return wrapped
 
+# bpo-35411: FTP tests of test_urllib2net randomly fail
+# with "425 Security: Bad IP connecting" on Travis CI
+skip_ftp_test_on_travis = unittest.skipIf('TRAVIS' in os.environ,
+                                          'bpo-35411: skip FTP test '
+                                          'on Travis CI')
+
+
 # Connecting to remote hosts is flaky.  Make it more robust by retrying
 # the connection several times.
 _urlopen_with_retry = _wrap_with_retry_thrice(urllib.request.urlopen,
@@ -95,6 +102,7 @@ class OtherNetworkTests(unittest.TestCase):
     # XXX The rest of these tests aren't very good -- they don't check much.
     # They do sometimes catch some major disasters, though.
 
+    @skip_ftp_test_on_travis
     def test_ftp(self):
         urls = [
             'ftp://www.pythontest.net/README',
@@ -290,6 +298,7 @@ class TimeoutTest(unittest.TestCase):
 
     FTP_HOST = 'ftp://www.pythontest.net/'
 
+    @skip_ftp_test_on_travis
     def test_ftp_basic(self):
         self.assertIsNone(socket.getdefaulttimeout())
         with support.transient_internet(self.FTP_HOST, timeout=None):
@@ -297,6 +306,7 @@ class TimeoutTest(unittest.TestCase):
             self.addCleanup(u.close)
             self.assertIsNone(u.fp.fp.raw._sock.gettimeout())
 
+    @skip_ftp_test_on_travis
     def test_ftp_default_timeout(self):
         self.assertIsNone(socket.getdefaulttimeout())
         with support.transient_internet(self.FTP_HOST):
@@ -308,6 +318,7 @@ class TimeoutTest(unittest.TestCase):
                 socket.setdefaulttimeout(None)
             self.assertEqual(u.fp.fp.raw._sock.gettimeout(), 60)
 
+    @skip_ftp_test_on_travis
     def test_ftp_no_timeout(self):
         self.assertIsNone(socket.getdefaulttimeout())
         with support.transient_internet(self.FTP_HOST):
@@ -319,6 +330,7 @@ class TimeoutTest(unittest.TestCase):
                 socket.setdefaulttimeout(None)
             self.assertIsNone(u.fp.fp.raw._sock.gettimeout())
 
+    @skip_ftp_test_on_travis
     def test_ftp_timeout(self):
         with support.transient_internet(self.FTP_HOST):
             u = _urlopen_with_retry(self.FTP_HOST, timeout=60)
index ddee1c3..be50b47 100644 (file)
@@ -879,6 +879,13 @@ class UrlParseTestCase(unittest.TestCase):
                                                           errors="ignore")
         self.assertEqual(result, [('key', '\u0141-')])
 
+    def test_parse_qsl_max_num_fields(self):
+        with self.assertRaises(ValueError):
+            urllib.parse.parse_qs('&'.join(['a=a']*11), max_num_fields=10)
+        with self.assertRaises(ValueError):
+            urllib.parse.parse_qs(';'.join(['a=a']*11), max_num_fields=10)
+        urllib.parse.parse_qs('&'.join(['a=a']*10), max_num_fields=10)
+
     def test_urlencode_sequences(self):
         # Other tests incidentally urlencode things; test non-covered cases:
         # Sequence and object values.
index c86f7a1..22a3b78 100644 (file)
@@ -35,7 +35,7 @@ def check_output(cmd, encoding=None):
     out, err = p.communicate()
     if p.returncode:
         raise subprocess.CalledProcessError(
-            p.returncode, cmd, None, out, err)
+            p.returncode, cmd, out, err)
     return out, err
 
 class BaseTest(unittest.TestCase):
@@ -243,6 +243,7 @@ class BasicTest(BaseTest):
             self.assertIn('include-system-site-packages = %s\n' % s, data)
 
     @unittest.skipUnless(can_symlink(), 'Needs symlinks')
+    @unittest.skipIf(os.name == 'nt', 'Symlinks are never used on Windows')
     def test_symlinking(self):
         """
         Test symlinking works as expected
index 7f3f3ff..a40a9a2 100644 (file)
@@ -960,12 +960,27 @@ class PyWarningsDisplayTests(WarningsDisplayTests, unittest.TestCase):
                 func()
             """))
 
-        res = assert_python_ok('-Wd', '-X', 'tracemalloc=2', support.TESTFN)
+        def run(*args):
+            res = assert_python_ok(*args)
+            stderr = res.err.decode('ascii', 'replace')
+            stderr = '\n'.join(stderr.splitlines())
 
-        stderr = res.err.decode('ascii', 'replace')
-        # normalize newlines
-        stderr = '\n'.join(stderr.splitlines())
-        stderr = re.sub('<.*>', '<...>', stderr)
+            # normalize newlines
+            stderr = re.sub('<.*>', '<...>', stderr)
+            return stderr
+
+        # tracemalloc disabled
+        stderr = run('-Wd', support.TESTFN)
+        expected = textwrap.dedent('''
+            {fname}:5: ResourceWarning: unclosed file <...>
+              f = None
+            ResourceWarning: Enable tracemalloc to get the object allocation traceback
+        ''')
+        expected = expected.format(fname=support.TESTFN).strip()
+        self.assertEqual(stderr, expected)
+
+        # tracemalloc enabled
+        stderr = run('-Wd', '-X', 'tracemalloc=2', support.TESTFN)
         expected = textwrap.dedent('''
             {fname}:5: ResourceWarning: unclosed file <...>
               f = None
index 71f2e27..519a943 100644 (file)
@@ -309,6 +309,24 @@ class ImportTest(unittest.TestCase):
             webbrowser = support.import_fresh_module('webbrowser')
             webbrowser.get()
 
+    def test_environment_preferred(self):
+        webbrowser = support.import_fresh_module('webbrowser')
+        try:
+            webbrowser.get()
+            least_preferred_browser = webbrowser.get(webbrowser._tryorder[-1]).name
+        except (webbrowser.Error, AttributeError, IndexError) as err:
+            self.skipTest(str(err))
+
+        with support.EnvironmentVarGuard() as env:
+            env["BROWSER"] = least_preferred_browser
+            webbrowser = support.import_fresh_module('webbrowser')
+            self.assertEqual(webbrowser.get().name, least_preferred_browser)
+
+        with support.EnvironmentVarGuard() as env:
+            env["BROWSER"] = sys.executable
+            webbrowser = support.import_fresh_module('webbrowser')
+            self.assertEqual(webbrowser.get().name, sys.executable)
+
 
 if __name__=='__main__':
     unittest.main()
index 088a04e..75c9c25 100644 (file)
@@ -2160,6 +2160,21 @@ class ElementTreeTypeTest(unittest.TestCase):
         mye = MyElement('joe')
         self.assertEqual(mye.newmethod(), 'joe')
 
+    def test_Element_subclass_find(self):
+        class MyElement(ET.Element):
+            pass
+
+        e = ET.Element('foo')
+        e.text = 'text'
+        sub = MyElement('bar')
+        sub.text = 'subtext'
+        e.append(sub)
+        self.assertEqual(e.findtext('bar'), 'subtext')
+        self.assertEqual(e.find('bar').tag, 'bar')
+        found = list(e.findall('bar'))
+        self.assertEqual(len(found), 1, found)
+        self.assertEqual(found[0].tag, 'bar')
+
 
 class ElementFindTest(unittest.TestCase):
     def test_find_simple(self):
index 765652f..e87de60 100644 (file)
@@ -117,6 +117,22 @@ class MiscTests(unittest.TestCase):
         elem.tail = X()
         elem.__setstate__({'tag': 42})  # shouldn't cause an assertion failure
 
+    def test_setstate_leaks(self):
+        # Test reference leaks
+        elem = cET.Element.__new__(cET.Element)
+        for i in range(100):
+            elem.__setstate__({'tag': 'foo', 'attrib': {'bar': 42},
+                               '_children': [cET.Element('child')],
+                               'text': 'text goes here',
+                               'tail': 'opposite of head'})
+
+        self.assertEqual(elem.tag, 'foo')
+        self.assertEqual(elem.text, 'text goes here')
+        self.assertEqual(elem.tail, 'opposite of head')
+        self.assertEqual(list(elem.attrib.items()), [('bar', 42)])
+        self.assertEqual(len(elem), 1)
+        self.assertEqual(elem[0].tag, 'child')
+
 
 @unittest.skipUnless(cET, 'requires _elementtree')
 class TestAliasWorking(unittest.TestCase):
index ff85f83..a6e8fb2 100644 (file)
@@ -3750,7 +3750,7 @@ class Spinbox(Widget, XView):
         select to commands. If the selection isn't currently in
         the spinbox, then a new selection is created to include
         the characters between index and the most recent selection
-        anchor point, inclusive. Returns an empty string.
+        anchor point, inclusive.
         """
         return self.selection("adjust", index)
 
@@ -3758,7 +3758,7 @@ class Spinbox(Widget, XView):
         """Clear the selection
 
         If the selection isn't in this widget then the
-        command has no effect. Returns an empty string.
+        command has no effect.
         """
         return self.selection("clear")
 
@@ -3766,9 +3766,9 @@ class Spinbox(Widget, XView):
         """Sets or gets the currently selected element.
 
         If a spinbutton element is specified, it will be
-        displayed depressed
+        displayed depressed.
         """
-        return self.selection("element", element)
+        return self.tk.call(self._w, 'selection', 'element', element)
 
 ###########################################################################
 
index 0d9a65a..467a0b6 100644 (file)
@@ -62,9 +62,9 @@ def requires_tcl(*version):
     def deco(test):
         @functools.wraps(test)
         def newtest(self):
-            if get_tk_patchlevel() < (8, 6, 5):
+            if get_tk_patchlevel() < version:
                 self.skipTest('requires Tcl version >= ' +
-                                '.'.join(map(str, get_tk_patchlevel())))
+                                '.'.join(map(str, version)))
             test(self)
         return newtest
     return deco
index e4c9d33..3fb6411 100644 (file)
@@ -474,6 +474,14 @@ class SpinboxTest(EntryTest, unittest.TestCase):
         self.assertRaises(TypeError, widget.bbox)
         self.assertRaises(TypeError, widget.bbox, 0, 1)
 
+    def test_selection_element(self):
+        widget = self.create()
+        self.assertEqual(widget.selection_element(), "none")
+        widget.selection_element("buttonup")
+        self.assertEqual(widget.selection_element(), "buttonup")
+        widget.selection_element("buttondown")
+        self.assertEqual(widget.selection_element(), "buttondown")
+
 
 @add_standard_options(StandardOptionsTests)
 class TextTest(AbstractWidgetTest, unittest.TestCase):
index 86b2101..0ed7ba9 100755 (executable)
@@ -299,7 +299,7 @@ class CoverageResults:
         try:
             outfile = open(path, "w", encoding=encoding)
         except OSError as err:
-            print(("trace: Could not open %r for writing: %s"
+            print(("trace: Could not open %r for writing: %s "
                                   "- skipping" % (path, err)), file=sys.stderr)
             return 0, 0
 
@@ -644,7 +644,7 @@ def main():
     grp = parser.add_argument_group('Filters',
             'Can be specified multiple times')
     grp.add_argument('--ignore-module', action='append', default=[],
-            help='Ignore the given module(s) and its submodules'
+            help='Ignore the given module(s) and its submodules '
                  '(if it is a package). Accepts comma separated list of '
                  'module names.')
     grp.add_argument('--ignore-dir', action='append', default=[],
index 9db564b..47a94f2 100644 (file)
@@ -1352,7 +1352,7 @@ class TurtleScreen(TurtleScreenBase):
         Arguments:
         fun -- a function with two arguments, the coordinates of the
                clicked point on the canvas.
-        num -- the number of the mouse-button, defaults to 1
+        btn -- the number of the mouse-button, defaults to 1
 
         Example (for a TurtleScreen instance named screen)
 
@@ -3526,7 +3526,7 @@ class RawTurtle(TPen, TNavigator):
         Arguments:
         fun --  a function with two arguments, to which will be assigned
                 the coordinates of the clicked point on the canvas.
-        num --  number of the mouse-button defaults to 1 (left mouse button).
+        btn --  number of the mouse-button defaults to 1 (left mouse button).
         add --  True or False. If True, new binding will be added, otherwise
                 it will replace a former binding.
 
@@ -3547,7 +3547,7 @@ class RawTurtle(TPen, TNavigator):
         Arguments:
         fun -- a function with two arguments, to which will be assigned
                 the coordinates of the clicked point on the canvas.
-        num --  number of the mouse-button defaults to 1 (left mouse button).
+        btn --  number of the mouse-button defaults to 1 (left mouse button).
 
         Example (for a MyTurtle instance named joe):
         >>> class MyTurtle(Turtle):
@@ -3572,7 +3572,7 @@ class RawTurtle(TPen, TNavigator):
         Arguments:
         fun -- a function with two arguments, to which will be assigned
                the coordinates of the clicked point on the canvas.
-        num -- number of the mouse-button defaults to 1 (left mouse button).
+        btn -- number of the mouse-button defaults to 1 (left mouse button).
 
         Every sequence of mouse-move-events on a turtle is preceded by a
         mouse-click event on that turtle.
index 445a424..8cf0d00 100644 (file)
@@ -130,7 +130,7 @@ def _type_check(arg, msg, is_argument=True):
     if (isinstance(arg, _GenericAlias) and
             arg.__origin__ in invalid_generic_forms):
         raise TypeError(f"{arg} is not valid as type argument")
-    if (isinstance(arg, _SpecialForm) and arg is not Any or
+    if (isinstance(arg, _SpecialForm) and arg not in (Any, NoReturn) or
             arg in (Generic, _Protocol)):
         raise TypeError(f"Plain {arg} is not valid as type argument")
     if isinstance(arg, (type, TypeVar, ForwardRef)):
@@ -1242,6 +1242,7 @@ ContextManager = _alias(contextlib.AbstractContextManager, T_co)
 AsyncContextManager = _alias(contextlib.AbstractAsyncContextManager, T_co)
 Dict = _alias(dict, (KT, VT), inst=False)
 DefaultDict = _alias(collections.defaultdict, (KT, VT))
+OrderedDict = _alias(collections.OrderedDict, (KT, VT))
 Counter = _alias(collections.Counter, T)
 ChainMap = _alias(collections.ChainMap, (KT, VT))
 Generator = _alias(collections.abc.Generator, (T_co, T_contra, V_co))
index cfc0d76..f641e38 100644 (file)
@@ -541,7 +541,7 @@ class NonCallableMock(Base):
             self._mock_side_effect = None
 
         for child in self._mock_children.values():
-            if isinstance(child, _SpecState):
+            if isinstance(child, _SpecState) or child is _deleted:
                 continue
             child.reset_mock(visited)
 
@@ -955,73 +955,77 @@ class CallableMixin(Base):
         self = _mock_self
         self.called = True
         self.call_count += 1
-        _new_name = self._mock_new_name
-        _new_parent = self._mock_new_parent
 
+        # handle call_args
         _call = _Call((args, kwargs), two=True)
         self.call_args = _call
         self.call_args_list.append(_call)
-        self.mock_calls.append(_Call(('', args, kwargs)))
 
         seen = set()
-        skip_next_dot = _new_name == '()'
+
+        # initial stuff for method_calls:
         do_method_calls = self._mock_parent is not None
-        name = self._mock_name
-        while _new_parent is not None:
-            this_mock_call = _Call((_new_name, args, kwargs))
-            if _new_parent._mock_new_name:
-                dot = '.'
-                if skip_next_dot:
-                    dot = ''
+        method_call_name = self._mock_name
 
-                skip_next_dot = False
-                if _new_parent._mock_new_name == '()':
-                    skip_next_dot = True
+        # initial stuff for mock_calls:
+        mock_call_name = self._mock_new_name
+        is_a_call = mock_call_name == '()'
+        self.mock_calls.append(_Call(('', args, kwargs)))
 
-                _new_name = _new_parent._mock_new_name + dot + _new_name
+        # follow up the chain of mocks:
+        _new_parent = self._mock_new_parent
+        while _new_parent is not None:
 
+            # handle method_calls:
             if do_method_calls:
-                if _new_name == name:
-                    this_method_call = this_mock_call
-                else:
-                    this_method_call = _Call((name, args, kwargs))
-                _new_parent.method_calls.append(this_method_call)
-
+                _new_parent.method_calls.append(_Call((method_call_name, args, kwargs)))
                 do_method_calls = _new_parent._mock_parent is not None
                 if do_method_calls:
-                    name = _new_parent._mock_name + '.' + name
+                    method_call_name = _new_parent._mock_name + '.' + method_call_name
 
+            # handle mock_calls:
+            this_mock_call = _Call((mock_call_name, args, kwargs))
             _new_parent.mock_calls.append(this_mock_call)
+
+            if _new_parent._mock_new_name:
+                if is_a_call:
+                    dot = ''
+                else:
+                    dot = '.'
+                is_a_call = _new_parent._mock_new_name == '()'
+                mock_call_name = _new_parent._mock_new_name + dot + mock_call_name
+
+            # follow the parental chain:
             _new_parent = _new_parent._mock_new_parent
 
-            # use ids here so as not to call __hash__ on the mocks
+            # check we're not in an infinite loop:
+            # ( use ids here so as not to call __hash__ on the mocks)
             _new_parent_id = id(_new_parent)
             if _new_parent_id in seen:
                 break
             seen.add(_new_parent_id)
 
-        ret_val = DEFAULT
         effect = self.side_effect
         if effect is not None:
             if _is_exception(effect):
                 raise effect
-
-            if not _callable(effect):
+            elif not _callable(effect):
                 result = next(effect)
                 if _is_exception(result):
                     raise result
-                if result is DEFAULT:
-                    result = self.return_value
+            else:
+                result = effect(*args, **kwargs)
+
+            if result is not DEFAULT:
                 return result
 
-            ret_val = effect(*args, **kwargs)
+        if self._mock_return_value is not DEFAULT:
+            return self.return_value
 
-        if (self._mock_wraps is not None and
-             self._mock_return_value is DEFAULT):
+        if self._mock_wraps is not None:
             return self._mock_wraps(*args, **kwargs)
-        if ret_val is DEFAULT:
-            ret_val = self.return_value
-        return ret_val
+
+        return self.return_value
 
 
 
@@ -1749,7 +1753,7 @@ _all_magics = _magics | _non_defaults
 
 _unsupported_magics = {
     '__getattr__', '__setattr__',
-    '__init__', '__new__', '__prepare__'
+    '__init__', '__new__', '__prepare__',
     '__instancecheck__', '__subclasscheck__',
     '__del__'
 }
@@ -2011,9 +2015,9 @@ class _Call(tuple):
 
     def __init__(self, value=(), name=None, parent=None, two=False,
                  from_kall=True):
-        self.name = name
-        self.parent = parent
-        self.from_kall = from_kall
+        self._mock_name = name
+        self._mock_parent = parent
+        self._mock_from_kall = from_kall
 
 
     def __eq__(self, other):
@@ -2030,6 +2034,10 @@ class _Call(tuple):
         else:
             self_name, self_args, self_kwargs = self
 
+        if (getattr(self, '_mock_parent', None) and getattr(other, '_mock_parent', None)
+                and self._mock_parent != other._mock_parent):
+            return False
+
         other_name = ''
         if len_other == 0:
             other_args, other_kwargs = (), {}
@@ -2071,17 +2079,17 @@ class _Call(tuple):
 
 
     def __call__(self, *args, **kwargs):
-        if self.name is None:
+        if self._mock_name is None:
             return _Call(('', args, kwargs), name='()')
 
-        name = self.name + '()'
-        return _Call((self.name, args, kwargs), name=name, parent=self)
+        name = self._mock_name + '()'
+        return _Call((self._mock_name, args, kwargs), name=name, parent=self)
 
 
     def __getattr__(self, attr):
-        if self.name is None:
+        if self._mock_name is None:
             return _Call(name=attr, from_kall=False)
-        name = '%s.%s' % (self.name, attr)
+        name = '%s.%s' % (self._mock_name, attr)
         return _Call(name=name, parent=self, from_kall=False)
 
 
@@ -2092,8 +2100,8 @@ class _Call(tuple):
         return self.__getattr__('index')(*args, **kwargs)
 
     def __repr__(self):
-        if not self.from_kall:
-            name = self.name or 'call'
+        if not self._mock_from_kall:
+            name = self._mock_name or 'call'
             if name.startswith('()'):
                 name = 'call%s' % name
             return name
@@ -2119,9 +2127,9 @@ class _Call(tuple):
         vals = []
         thing = self
         while thing is not None:
-            if thing.from_kall:
+            if thing._mock_from_kall:
                 vals.append(thing)
-            thing = thing.parent
+            thing = thing._mock_parent
         return _CallList(reversed(vals))
 
 
@@ -2423,15 +2431,14 @@ class PropertyMock(Mock):
 
 
 def seal(mock):
-    """Disable the automatic generation of "submocks"
+    """Disable the automatic generation of child mocks.
 
     Given an input Mock, seals it to ensure no further mocks will be generated
     when accessing an attribute that was not already defined.
 
-    Submocks are defined as all mocks which were created DIRECTLY from the
-    parent. If a mock is assigned to an attribute of an existing mock,
-    it is not considered a submock.
-
+    The operation recursively seals the mock passed in, meaning that
+    the mock itself, any mocks generated by accessing one of its attributes,
+    and all assigned mocks without a name or spec will be sealed.
     """
     mock._mock_sealed = True
     for attr in dir(mock):
index af1ce7e..34474c4 100644 (file)
@@ -128,7 +128,7 @@ class TestCallable(unittest.TestCase):
                     result.foo.assert_called_once_with(3, 2, 1)
 
 
-    def test_create_autopsec(self):
+    def test_create_autospec(self):
         mock = create_autospec(X)
         instance = mock()
         self.assertRaises(TypeError, instance)
index 7919482..9388311 100644 (file)
@@ -8,6 +8,7 @@ from unittest.mock import (
 )
 
 from datetime import datetime
+from functools import partial
 
 class SomeClass(object):
     def one(self, a, b):
@@ -269,6 +270,22 @@ class CallTest(unittest.TestCase):
         self.assertEqual(mock.mock_calls, last_call.call_list())
 
 
+    def test_extended_not_equal(self):
+        a = call(x=1).foo
+        b = call(x=2).foo
+        self.assertEqual(a, a)
+        self.assertEqual(b, b)
+        self.assertNotEqual(a, b)
+
+
+    def test_nested_calls_not_equal(self):
+        a = call(x=1).foo().bar
+        b = call(x=2).foo().bar
+        self.assertEqual(a, a)
+        self.assertEqual(b, b)
+        self.assertNotEqual(a, b)
+
+
     def test_call_list(self):
         mock = MagicMock()
         mock(1)
@@ -871,6 +888,19 @@ class SpecSignatureTest(unittest.TestCase):
         mocked.assert_called_once_with(4, 5, 6)
 
 
+    def test_autospec_getattr_partial_function(self):
+        # bpo-32153 : getattr returning partial functions without
+        # __name__ should not create AttributeError in create_autospec
+        class Foo:
+
+            def __getattr__(self, attribute):
+                return partial(lambda name: name, attribute)
+
+        proxy = Foo()
+        autospec = create_autospec(proxy)
+        self.assertFalse(hasattr(autospec, '__name__'))
+
+
 class TestCallList(unittest.TestCase):
 
     def test_args_list_contains_call_list(self):
index c7bfa27..49ecbb4 100644 (file)
@@ -8,7 +8,7 @@ from unittest import mock
 from unittest.mock import (
     call, DEFAULT, patch, sentinel,
     MagicMock, Mock, NonCallableMock,
-    NonCallableMagicMock, _CallList,
+    NonCallableMagicMock, _Call, _CallList,
     create_autospec
 )
 
@@ -549,6 +549,16 @@ class MockTest(unittest.TestCase):
         real.assert_called_with(1, 2, fish=3)
 
 
+    def test_wraps_prevents_automatic_creation_of_mocks(self):
+        class Real(object):
+            pass
+
+        real = Real()
+        mock = Mock(wraps=real)
+
+        self.assertRaises(AttributeError, lambda: mock.new_attr())
+
+
     def test_wraps_call_with_nondefault_return_value(self):
         real = Mock()
 
@@ -575,6 +585,118 @@ class MockTest(unittest.TestCase):
         self.assertEqual(result, Real.attribute.frog())
 
 
+    def test_customize_wrapped_object_with_side_effect_iterable_with_default(self):
+        class Real(object):
+            def method(self):
+                return sentinel.ORIGINAL_VALUE
+
+        real = Real()
+        mock = Mock(wraps=real)
+        mock.method.side_effect = [sentinel.VALUE1, DEFAULT]
+
+        self.assertEqual(mock.method(), sentinel.VALUE1)
+        self.assertEqual(mock.method(), sentinel.ORIGINAL_VALUE)
+        self.assertRaises(StopIteration, mock.method)
+
+
+    def test_customize_wrapped_object_with_side_effect_iterable(self):
+        class Real(object):
+            def method(self):
+                raise NotImplementedError()
+
+        real = Real()
+        mock = Mock(wraps=real)
+        mock.method.side_effect = [sentinel.VALUE1, sentinel.VALUE2]
+
+        self.assertEqual(mock.method(), sentinel.VALUE1)
+        self.assertEqual(mock.method(), sentinel.VALUE2)
+        self.assertRaises(StopIteration, mock.method)
+
+
+    def test_customize_wrapped_object_with_side_effect_exception(self):
+        class Real(object):
+            def method(self):
+                raise NotImplementedError()
+
+        real = Real()
+        mock = Mock(wraps=real)
+        mock.method.side_effect = RuntimeError
+
+        self.assertRaises(RuntimeError, mock.method)
+
+
+    def test_customize_wrapped_object_with_side_effect_function(self):
+        class Real(object):
+            def method(self):
+                raise NotImplementedError()
+
+        def side_effect():
+            return sentinel.VALUE
+
+        real = Real()
+        mock = Mock(wraps=real)
+        mock.method.side_effect = side_effect
+
+        self.assertEqual(mock.method(), sentinel.VALUE)
+
+
+    def test_customize_wrapped_object_with_return_value(self):
+        class Real(object):
+            def method(self):
+                raise NotImplementedError()
+
+        real = Real()
+        mock = Mock(wraps=real)
+        mock.method.return_value = sentinel.VALUE
+
+        self.assertEqual(mock.method(), sentinel.VALUE)
+
+
+    def test_customize_wrapped_object_with_return_value_and_side_effect(self):
+        # side_effect should always take precedence over return_value.
+        class Real(object):
+            def method(self):
+                raise NotImplementedError()
+
+        real = Real()
+        mock = Mock(wraps=real)
+        mock.method.side_effect = [sentinel.VALUE1, sentinel.VALUE2]
+        mock.method.return_value = sentinel.WRONG_VALUE
+
+        self.assertEqual(mock.method(), sentinel.VALUE1)
+        self.assertEqual(mock.method(), sentinel.VALUE2)
+        self.assertRaises(StopIteration, mock.method)
+
+
+    def test_customize_wrapped_object_with_return_value_and_side_effect2(self):
+        # side_effect can return DEFAULT to default to return_value
+        class Real(object):
+            def method(self):
+                raise NotImplementedError()
+
+        real = Real()
+        mock = Mock(wraps=real)
+        mock.method.side_effect = lambda: DEFAULT
+        mock.method.return_value = sentinel.VALUE
+
+        self.assertEqual(mock.method(), sentinel.VALUE)
+
+
+    def test_customize_wrapped_object_with_return_value_and_side_effect_default(self):
+        class Real(object):
+            def method(self):
+                raise NotImplementedError()
+
+        real = Real()
+        mock = Mock(wraps=real)
+        mock.method.side_effect = [sentinel.VALUE1, DEFAULT]
+        mock.method.return_value = sentinel.RETURN
+
+        self.assertEqual(mock.method(), sentinel.VALUE1)
+        self.assertEqual(mock.method(), sentinel.RETURN)
+        self.assertRaises(StopIteration, mock.method)
+
+
     def test_exceptional_side_effect(self):
         mock = Mock(side_effect=AttributeError)
         self.assertRaises(AttributeError, mock)
@@ -916,6 +1038,57 @@ class MockTest(unittest.TestCase):
                              call().__int__().call_list())
 
 
+    def test_child_mock_call_equal(self):
+        m = Mock()
+        result = m()
+        result.wibble()
+        # parent looks like this:
+        self.assertEqual(m.mock_calls, [call(), call().wibble()])
+        # but child should look like this:
+        self.assertEqual(result.mock_calls, [call.wibble()])
+
+
+    def test_mock_call_not_equal_leaf(self):
+        m = Mock()
+        m.foo().something()
+        self.assertNotEqual(m.mock_calls[1], call.foo().different())
+        self.assertEqual(m.mock_calls[0], call.foo())
+
+
+    def test_mock_call_not_equal_non_leaf(self):
+        m = Mock()
+        m.foo().bar()
+        self.assertNotEqual(m.mock_calls[1], call.baz().bar())
+        self.assertNotEqual(m.mock_calls[0], call.baz())
+
+
+    def test_mock_call_not_equal_non_leaf_params_different(self):
+        m = Mock()
+        m.foo(x=1).bar()
+        # This isn't ideal, but there's no way to fix it without breaking backwards compatibility:
+        self.assertEqual(m.mock_calls[1], call.foo(x=2).bar())
+
+
+    def test_mock_call_not_equal_non_leaf_attr(self):
+        m = Mock()
+        m.foo.bar()
+        self.assertNotEqual(m.mock_calls[0], call.baz.bar())
+
+
+    def test_mock_call_not_equal_non_leaf_call_versus_attr(self):
+        m = Mock()
+        m.foo.bar()
+        self.assertNotEqual(m.mock_calls[0], call.foo().bar())
+
+
+    def test_mock_call_repr(self):
+        m = Mock()
+        m.foo().bar().baz.bob()
+        self.assertEqual(repr(m.mock_calls[0]), 'call.foo()')
+        self.assertEqual(repr(m.mock_calls[1]), 'call.foo().bar()')
+        self.assertEqual(repr(m.mock_calls[2]), 'call.foo().bar().baz.bob()')
+
+
     def test_subclassing(self):
         class Subclass(Mock):
             pass
@@ -1566,6 +1739,16 @@ class MockTest(unittest.TestCase):
             self.assertRaises(AttributeError, getattr, mock, 'f')
 
 
+    def test_reset_mock_does_not_raise_on_attr_deletion(self):
+        # bpo-31177: reset_mock should not raise AttributeError when attributes
+        # were deleted in a mock instance
+        mock = Mock()
+        mock.child = True
+        del mock.child
+        mock.reset_mock()
+        self.assertFalse(hasattr(mock, 'child'))
+
+
     def test_class_assignable(self):
         for mock in Mock(), MagicMock():
             self.assertNotIsInstance(mock, int)
@@ -1574,6 +1757,20 @@ class MockTest(unittest.TestCase):
             self.assertIsInstance(mock, int)
             mock.foo
 
+    def test_name_attribute_of_call(self):
+        # bpo-35357: _Call should not disclose any attributes whose names
+        # may clash with popular ones (such as ".name")
+        self.assertIsNotNone(call.name)
+        self.assertEqual(type(call.name), _Call)
+        self.assertEqual(type(call.name().name), _Call)
+
+    def test_parent_attribute_of_call(self):
+        # bpo-35357: _Call should not disclose any attributes whose names
+        # may clash with popular ones (such as ".parent")
+        self.assertIsNotNone(call.parent)
+        self.assertEqual(type(call.parent), _Call)
+        self.assertEqual(type(call.parent().parent), _Call)
+
 
 if __name__ == '__main__':
     unittest.main()
index fe4ecef..f052257 100644 (file)
@@ -9,6 +9,7 @@ import unittest
 from unittest.test.testmock import support
 from unittest.test.testmock.support import SomeClass, is_instance
 
+from test.test_importlib.util import uncache
 from unittest.mock import (
     NonCallableMock, CallableMixin, sentinel,
     MagicMock, Mock, NonCallableMagicMock, patch, _patch,
@@ -1660,20 +1661,19 @@ class PatchTest(unittest.TestCase):
 
 
     def test_patch_imports_lazily(self):
-        sys.modules.pop('squizz', None)
-
         p1 = patch('squizz.squozz')
         self.assertRaises(ImportError, p1.start)
 
-        squizz = Mock()
-        squizz.squozz = 6
-        sys.modules['squizz'] = squizz
-        p1 = patch('squizz.squozz')
-        squizz.squozz = 3
-        p1.start()
-        p1.stop()
-        self.assertEqual(squizz.squozz, 3)
+        with uncache('squizz'):
+            squizz = Mock()
+            sys.modules['squizz'] = squizz
 
+            squizz.squozz = 6
+            p1 = patch('squizz.squozz')
+            squizz.squozz = 3
+            p1.start()
+            p1.stop()
+        self.assertEqual(squizz.squozz, 3)
 
     def test_patch_propogrates_exc_on_exit(self):
         class holder:
@@ -1696,7 +1696,12 @@ class PatchTest(unittest.TestCase):
         def test(mock):
             raise RuntimeError
 
-        self.assertRaises(RuntimeError, test)
+        with uncache('squizz'):
+            squizz = Mock()
+            sys.modules['squizz'] = squizz
+
+            self.assertRaises(RuntimeError, test)
+
         self.assertIs(holder.exc_info[0], RuntimeError)
         self.assertIsNotNone(holder.exc_info[1],
                             'exception value not propgated')
index 58460f9..f691ab7 100644 (file)
@@ -623,7 +623,7 @@ def unquote(string, encoding='utf-8', errors='replace'):
 
 
 def parse_qs(qs, keep_blank_values=False, strict_parsing=False,
-             encoding='utf-8', errors='replace'):
+             encoding='utf-8', errors='replace', max_num_fields=None):
     """Parse a query given as a string argument.
 
         Arguments:
@@ -644,11 +644,15 @@ def parse_qs(qs, keep_blank_values=False, strict_parsing=False,
         encoding and errors: specify how to decode percent-encoded sequences
             into Unicode characters, as accepted by the bytes.decode() method.
 
+        max_num_fields: int. If set, then throws a ValueError if there
+            are more than n fields read by parse_qsl().
+
         Returns a dictionary.
     """
     parsed_result = {}
     pairs = parse_qsl(qs, keep_blank_values, strict_parsing,
-                      encoding=encoding, errors=errors)
+                      encoding=encoding, errors=errors,
+                      max_num_fields=max_num_fields)
     for name, value in pairs:
         if name in parsed_result:
             parsed_result[name].append(value)
@@ -658,7 +662,7 @@ def parse_qs(qs, keep_blank_values=False, strict_parsing=False,
 
 
 def parse_qsl(qs, keep_blank_values=False, strict_parsing=False,
-              encoding='utf-8', errors='replace'):
+              encoding='utf-8', errors='replace', max_num_fields=None):
     """Parse a query given as a string argument.
 
         Arguments:
@@ -678,9 +682,21 @@ def parse_qsl(qs, keep_blank_values=False, strict_parsing=False,
         encoding and errors: specify how to decode percent-encoded sequences
             into Unicode characters, as accepted by the bytes.decode() method.
 
+        max_num_fields: int. If set, then throws a ValueError
+            if there are more than n fields read by parse_qsl().
+
         Returns a list, as G-d intended.
     """
     qs, _coerce_result = _coerce_args(qs)
+
+    # If max_num_fields is defined then check that the number of fields
+    # is less than max_num_fields. This prevents a memory exhaustion DOS
+    # attack via post bodies with many fields.
+    if max_num_fields is not None:
+        num_fields = 1 + qs.count('&') + qs.count(';')
+        if max_num_fields < num_fields:
+            raise ValueError('Max number of fields exceeded')
+
     pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')]
     r = []
     for name_value in pairs:
index 5b962f7..d38f725 100644 (file)
@@ -198,7 +198,7 @@ def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
     global _opener
     if cafile or capath or cadefault:
         import warnings
-        warnings.warn("cafile, cpath and cadefault are deprecated, use a "
+        warnings.warn("cafile, capath and cadefault are deprecated, use a "
                       "custom context instead.", DeprecationWarning, 2)
         if context is not None:
             raise ValueError(
index 716129d..5438b0d 100644 (file)
@@ -9,6 +9,7 @@ import os
 import shutil
 import subprocess
 import sys
+import sysconfig
 import types
 
 logger = logging.getLogger(__name__)
@@ -63,10 +64,11 @@ class EnvBuilder:
         self.system_site_packages = False
         self.create_configuration(context)
         self.setup_python(context)
+        if not self.upgrade:
+            self.setup_scripts(context)
         if self.with_pip:
             self._setup_pip(context)
         if not self.upgrade:
-            self.setup_scripts(context)
             self.post_setup(context)
         if true_system_site_packages:
             # We had set it to False before, now
@@ -157,14 +159,6 @@ class EnvBuilder:
             f.write('include-system-site-packages = %s\n' % incl)
             f.write('version = %d.%d.%d\n' % sys.version_info[:3])
 
-    if os.name == 'nt':
-        def include_binary(self, f):
-            if f.endswith(('.pyd', '.dll')):
-                result = True
-            else:
-                result = f.startswith('python') and f.endswith('.exe')
-            return result
-
     def symlink_or_copy(self, src, dst, relative_symlinks_ok=False):
         """
         Try symlinking a file, and if that fails, fall back to copying.
@@ -194,9 +188,9 @@ class EnvBuilder:
         binpath = context.bin_path
         path = context.env_exe
         copier = self.symlink_or_copy
-        copier(context.executable, path)
         dirname = context.python_dir
         if os.name != 'nt':
+            copier(context.executable, path)
             if not os.path.islink(path):
                 os.chmod(path, 0o755)
             for suffix in ('python', 'python3'):
@@ -208,32 +202,33 @@ class EnvBuilder:
                     if not os.path.islink(path):
                         os.chmod(path, 0o755)
         else:
-            subdir = 'DLLs'
-            include = self.include_binary
-            files = [f for f in os.listdir(dirname) if include(f)]
-            for f in files:
-                src = os.path.join(dirname, f)
-                dst = os.path.join(binpath, f)
-                if dst != context.env_exe:  # already done, above
-                    copier(src, dst)
-            dirname = os.path.join(dirname, subdir)
-            if os.path.isdir(dirname):
-                files = [f for f in os.listdir(dirname) if include(f)]
-                for f in files:
-                    src = os.path.join(dirname, f)
-                    dst = os.path.join(binpath, f)
-                    copier(src, dst)
-            # copy init.tcl over
-            for root, dirs, files in os.walk(context.python_dir):
-                if 'init.tcl' in files:
-                    tcldir = os.path.basename(root)
-                    tcldir = os.path.join(context.env_dir, 'Lib', tcldir)
-                    if not os.path.exists(tcldir):
-                        os.makedirs(tcldir)
-                    src = os.path.join(root, 'init.tcl')
-                    dst = os.path.join(tcldir, 'init.tcl')
-                    shutil.copyfile(src, dst)
-                    break
+            # For normal cases, the venvlauncher will be copied from
+            # our scripts folder. For builds, we need to copy it
+            # manually.
+            if sysconfig.is_python_build(True):
+                suffix = '.exe'
+                if context.python_exe.lower().endswith('_d.exe'):
+                    suffix = '_d.exe'
+
+                src = os.path.join(dirname, "venvlauncher" + suffix)
+                dst = os.path.join(binpath, context.python_exe)
+                copier(src, dst)
+
+                src = os.path.join(dirname, "venvwlauncher" + suffix)
+                dst = os.path.join(binpath, "pythonw" + suffix)
+                copier(src, dst)
+
+                # copy init.tcl over
+                for root, dirs, files in os.walk(context.python_dir):
+                    if 'init.tcl' in files:
+                        tcldir = os.path.basename(root)
+                        tcldir = os.path.join(context.env_dir, 'Lib', tcldir)
+                        if not os.path.exists(tcldir):
+                            os.makedirs(tcldir)
+                        src = os.path.join(root, 'init.tcl')
+                        dst = os.path.join(tcldir, 'init.tcl')
+                        shutil.copyfile(src, dst)
+                        break
 
     def _setup_pip(self, context):
         """Installs or upgrades pip in a virtual environment"""
@@ -320,7 +315,7 @@ class EnvBuilder:
                 dstfile = os.path.join(dstdir, f)
                 with open(srcfile, 'rb') as f:
                     data = f.read()
-                if not srcfile.endswith('.exe'):
+                if not srcfile.endswith(('.exe', '.pdb')):
                     try:
                         data = data.decode('utf-8')
                         data = self.replace_variables(data, context)
index 81f9864..ae4295e 100644 (file)
@@ -33,9 +33,8 @@ def _showwarnmsg_impl(msg):
         pass
 
 def _formatwarnmsg_impl(msg):
-    s =  ("%s:%s: %s: %s\n"
-          % (msg.filename, msg.lineno, msg.category.__name__,
-             msg.message))
+    category = msg.category.__name__
+    s =  f"{msg.filename}:{msg.lineno}: {category}: {msg.message}\n"
 
     if msg.line is None:
         try:
@@ -55,11 +54,20 @@ def _formatwarnmsg_impl(msg):
     if msg.source is not None:
         try:
             import tracemalloc
-            tb = tracemalloc.get_object_traceback(msg.source)
+        # Logging a warning should not raise a new exception:
+        # catch Exception, not only ImportError and RecursionError.
         except Exception:
-            # When a warning is logged during Python shutdown, tracemalloc
-            # and the import machinery don't work anymore
+            # don't suggest to enable tracemalloc if it's not available
+            tracing = True
             tb = None
+        else:
+            tracing = tracemalloc.is_tracing()
+            try:
+                tb = tracemalloc.get_object_traceback(msg.source)
+            except Exception:
+                # When a warning is logged during Python shutdown, tracemalloc
+                # and the import machinery don't work anymore
+                tb = None
 
         if tb is not None:
             s += 'Object allocated at (most recent call last):\n'
@@ -77,6 +85,9 @@ def _formatwarnmsg_impl(msg):
                 if line:
                     line = line.strip()
                     s += '    %s\n' % line
+        elif not tracing:
+            s += (f'{category}: Enable tracemalloc to get the object '
+                  f'allocation traceback\n')
     return s
 
 # Keep a reference to check if the function was replaced
index 1e27c83..82bff83 100755 (executable)
@@ -86,7 +86,7 @@ def open_new_tab(url):
     return open(url, 2)
 
 
-def _synthesize(browser, *, preferred=True):
+def _synthesize(browser, *, preferred=False):
     """Attempt to synthesize a controller base on existing controllers.
 
     This is useful to create a controller when a user specifies a path to
@@ -563,7 +563,7 @@ def register_standard_browsers():
         # and prepend to _tryorder
         for cmdline in userchoices:
             if cmdline != '':
-                cmd = _synthesize(cmdline, preferred=False)
+                cmd = _synthesize(cmdline, preferred=True)
                 if cmd[1] is None:
                     register(cmdline, None, GenericBrowser(cmdline), preferred=True)
 
index 8c3d901..69c17ee 100644 (file)
@@ -6,6 +6,8 @@ registerDOMImplementation should be imported from xml.dom."""
 # should be published by posting to xml-sig@python.org, and are
 # subsequently recorded in this file.
 
+import sys
+
 well_known_implementations = {
     'minidom':'xml.dom.minidom',
     '4DOM': 'xml.dom.DOMImplementation',
@@ -55,7 +57,7 @@ def getDOMImplementation(name=None, features=()):
         return mod.getDOMImplementation()
     elif name:
         return registered[name]()
-    elif "PYTHON_DOM" in os.environ:
+    elif not sys.flags.ignore_environment and "PYTHON_DOM" in os.environ:
         return getDOMImplementation(name = os.environ["PYTHON_DOM"])
 
     # User did not specify a name, try implementations in arbitrary
index a5d813f..24957ea 100644 (file)
@@ -1318,7 +1318,7 @@ class DocumentType(Identified, Childless, Node):
                     entity.encoding = e.encoding
                     entity.version = e.version
                     clone.entities._seq.append(entity)
-                    e._call_user_data_handler(operation, n, entity)
+                    e._call_user_data_handler(operation, e, entity)
             self._call_user_data_handler(operation, self, clone)
             return clone
         else:
@@ -1921,7 +1921,7 @@ def _clone_node(node, deep, newOwnerDocument):
                 entity.ownerDocument = newOwnerDocument
                 clone.entities._seq.append(entity)
                 if hasattr(e, '_call_user_data_handler'):
-                    e._call_user_data_handler(operation, n, entity)
+                    e._call_user_data_handler(operation, e, entity)
     else:
         # Note the cloning of Document and DocumentType nodes is
         # implementation specific.  minidom handles those cases
index ef67ae6..13f6cf5 100644 (file)
@@ -58,7 +58,7 @@ if _false:
     import xml.sax.expatreader
 
 import os, sys
-if "PY_SAX_PARSER" in os.environ:
+if not sys.flags.ignore_environment and "PY_SAX_PARSER" in os.environ:
     default_parser_list = os.environ["PY_SAX_PARSER"].split(",")
 del os
 
index a438785..d2d3b69 100644 (file)
@@ -402,7 +402,7 @@ class ZipInfo (object):
         return ''.join(result)
 
     def FileHeader(self, zip64=None):
-        """Return the per-file header as a string."""
+        """Return the per-file header as a bytes object."""
         dt = self.date_time
         dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
         dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2)
@@ -1424,7 +1424,7 @@ class ZipFile:
         self._didModify = True
 
     def read(self, name, pwd=None):
-        """Return file bytes (as a string) for name."""
+        """Return file bytes for name."""
         with self.open(name, "r", pwd) as fp:
             return fp.read()
 
index 1548878..5d11bbb 100755 (executable)
@@ -215,9 +215,9 @@ def library_recipes():
 
     result.extend([
           dict(
-              name="OpenSSL 1.1.0i",
-              url="https://www.openssl.org/source/openssl-1.1.0i.tar.gz",
-              checksum='9495126aafd2659d357ea66a969c3fe1',
+              name="OpenSSL 1.1.0j",
+              url="https://www.openssl.org/source/openssl-1.1.0j.tar.gz",
+              checksum='b4ca5b78ae6ae79da80790b30dbedbdc',
               buildrecipe=build_universal_openssl,
               configure=None,
               install=None,
@@ -227,9 +227,9 @@ def library_recipes():
     if internalTk():
         result.extend([
           dict(
-              name="Tcl 8.6.8",
-              url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tcl8.6.8-src.tar.gz",
-              checksum='81656d3367af032e0ae6157eff134f89',
+              name="Tcl 8.6.9",
+              url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tcl8.6.9-src.tar.gz",
+              checksum='aa0a121d95a0e7b73a036f26028538d4',
               buildDir="unix",
               configure_pre=[
                     '--enable-shared',
@@ -243,12 +243,9 @@ def library_recipes():
                   },
               ),
           dict(
-              name="Tk 8.6.8",
-              url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tk8.6.8-src.tar.gz",
-              checksum='5e0faecba458ee1386078fb228d008ba',
-              patches=[
-                  "tk868_on_10_8_10_9.patch",
-                   ],
+              name="Tk 8.6.9.1",
+              url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tk8.6.9.1-src.tar.gz",
+              checksum='9efe3976468352dc894dae0c4e785a8e',
               buildDir="unix",
               configure_pre=[
                     '--enable-aqua',
@@ -709,6 +706,7 @@ def extractArchive(builddir, archiveName):
     work for current Tcl and Tk source releases where the basename of
     the archive ends with "-src" but the uncompressed directory does not.
     For now, just special case Tcl and Tk tar.gz downloads.
+    Another special case: the tk8.6.9.1 tarball extracts to tk8.6.9.
     """
     curdir = os.getcwd()
     try:
@@ -718,6 +716,8 @@ def extractArchive(builddir, archiveName):
             if ((retval.startswith('tcl') or retval.startswith('tk'))
                     and retval.endswith('-src')):
                 retval = retval[:-4]
+                if retval == 'tk8.6.9.1':
+                    retval = 'tk8.6.9'
             if os.path.exists(retval):
                 shutil.rmtree(retval)
             fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
@@ -1518,16 +1518,27 @@ def buildDMG():
     imagepath = imagepath + '.dmg'
 
     os.mkdir(outdir)
+
+    # Try to mitigate race condition in certain versions of macOS, e.g. 10.9,
+    # when hdiutil create fails with  "Resource busy".  For now, just retry
+    # the create a few times and hope that it eventually works.
+
     volname='Python %s'%(getFullVersion())
-    runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
+    cmd = ("hdiutil create -format UDRW -volname %s -srcfolder %s -size 100m %s"%(
             shellQuote(volname),
             shellQuote(os.path.join(WORKDIR, 'installer')),
             shellQuote(imagepath + ".tmp.dmg" )))
-
-    # Try to mitigate race condition in certain versions of macOS, e.g. 10.9,
-    # when hdiutil fails with  "Resource busy"
-
-    time.sleep(10)
+    for i in range(5):
+        fd = os.popen(cmd, 'r')
+        data = fd.read()
+        xit = fd.close()
+        if not xit:
+            break
+        sys.stdout.write(data)
+        print(" -- retrying hdiutil create")
+        time.sleep(5)
+    else:
+        raise RuntimeError("command failed: %s"%(commandline,))
 
     if not os.path.exists(os.path.join(WORKDIR, "mnt")):
         os.mkdir(os.path.join(WORKDIR, "mnt"))
index 8e1847e..0d12478 100644 (file)
@@ -1,23 +1,28 @@
-{\rtf1\ansi\ansicpg1252\cocoartf1561\cocoasubrtf400
-\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\rtf1\ansi\ansicpg1252\cocoartf1671\cocoasubrtf100
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fswiss\fcharset0 Helvetica-Bold;\f2\fmodern\fcharset0 CourierNewPSMT;
+}
 {\colortbl;\red255\green255\blue255;}
 {\*\expandedcolortbl;;}
 \paperw11905\paperh16837\margl1440\margr1440\vieww12200\viewh10880\viewkind0
 \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\partightenfactor0
 
 \f0\fs24 \cf0 This package will install 
-\b Python $FULL_VERSION
-\b0  for 
-\b macOS $MACOSX_DEPLOYMENT_TARGET
-\b0 .\
+\f1\b Python $FULL_VERSION
+\f0\b0  for 
+\f1\b macOS $MACOSX_DEPLOYMENT_TARGET
+\f0\b0 .\
 \
 
-\b Python for macOS
-\b0  consists of the Python programming language interpreter, plus a set of programs to allow easy access to it for macOS users including an integrated development environment 
-\b IDLE
-\b0 .\
+\f1\b Python for macOS
+\f0\b0  consists of the Python programming language interpreter, plus a set of programs to allow easy access to it for macOS users including an integrated development environment 
+\f1\b IDLE
+\f0\b0 .\
+\
+At the end of this install, click on 
+\f2 Install Certificates
+\f0  for SSL root certificates\
 \
 
-\b NEW in 3.7.0: 
-\b0 two installer variants (10.9+ 64-bit-only, 10.6+ 64-/32-bit), built-in Tcl/Tk 8.6 support (no additional third-party downloads!), OpenSSL 1.1.0, and more!\
+\f1\b NEW in 3.7.0: 
+\f0\b0 two installer variants (10.9+ 64-bit-only, 10.6+ 64-/32-bit), built-in Tcl/Tk 8.6 support (no additional third-party downloads!), OpenSSL 1.1.0, and more!\
 }
\ No newline at end of file
diff --git a/Mac/BuildScript/tk868_on_10_8_10_9.patch b/Mac/BuildScript/tk868_on_10_8_10_9.patch
deleted file mode 100644 (file)
index 8fe1060..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-Fix build failure with +quartz variant on OS X 10.8 and 10.9.
-Even though Gestalt was deprecated in OS X 10.8, it should work fine
-through OS X 10.9, and its replacement NSOperatingSystemVersion was
-not introduced until OS X 10.10.
-
-Patch from MacPorts project and reported upstream:
-https://trac.macports.org/ticket/55649
---- tk8.6.8/macosx/tkMacOSXXStubs.c.orig       2017-12-06 09:25:08.000000000 -0600
-+++ tk8.6.8-patched/macosx/tkMacOSXXStubs.c    2018-01-06 19:34:17.000000000 -0600
-@@ -175,7 +175,7 @@
-     {
-       int major, minor, patch;
--#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080
-+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000
-       Gestalt(gestaltSystemVersionMajor, (SInt32*)&major);
-       Gestalt(gestaltSystemVersionMinor, (SInt32*)&minor);
-       Gestalt(gestaltSystemVersionBugFix, (SInt32*)&patch);
index 4c23c0e..2d2e11f 100644 (file)
@@ -85,6 +85,10 @@ CONFIGURE_CFLAGS=    @CFLAGS@
 # Use it when a compiler flag should _not_ be part of the distutils CFLAGS
 # once Python is installed (Issue #21121).
 CONFIGURE_CFLAGS_NODIST=@CFLAGS_NODIST@
+# LDFLAGS_NODIST is used in the same manner as CFLAGS_NODIST.
+# Use it when a linker flag should _not_ be part of the distutils LDFLAGS
+# once Python is installed (bpo-35257)
+CONFIGURE_LDFLAGS_NODIST=@LDFLAGS_NODIST@
 CONFIGURE_CPPFLAGS=    @CPPFLAGS@
 CONFIGURE_LDFLAGS=     @LDFLAGS@
 # Avoid assigning CFLAGS, LDFLAGS, etc. so users can use them on the
@@ -97,6 +101,7 @@ PY_CFLAGS_NODIST=$(CONFIGURE_CFLAGS_NODIST) $(CFLAGS_NODIST)
 # environment variables
 PY_CPPFLAGS=   $(BASECPPFLAGS) -I. -I$(srcdir)/Include $(CONFIGURE_CPPFLAGS) $(CPPFLAGS)
 PY_LDFLAGS=    $(CONFIGURE_LDFLAGS) $(LDFLAGS)
+PY_LDFLAGS_NODIST=$(CONFIGURE_LDFLAGS_NODIST) $(LDFLAGS_NODIST)
 NO_AS_NEEDED=  @NO_AS_NEEDED@
 LDLAST=                @LDLAST@
 SGI_ABI=       @SGI_ABI@
@@ -107,7 +112,10 @@ ARFLAGS=   @ARFLAGS@
 CFLAGSFORSHARED=@CFLAGSFORSHARED@
 # C flags used for building the interpreter object files
 PY_STDMODULE_CFLAGS= $(PY_CFLAGS) $(PY_CFLAGS_NODIST) $(PY_CPPFLAGS) $(CFLAGSFORSHARED)
+PY_BUILTIN_MODULE_CFLAGS= $(PY_STDMODULE_CFLAGS) -DPy_BUILD_CORE_BUILTIN
 PY_CORE_CFLAGS=        $(PY_STDMODULE_CFLAGS) -DPy_BUILD_CORE
+# Linker flags used for building the interpreter object files
+PY_CORE_LDFLAGS=$(PY_LDFLAGS) $(PY_LDFLAGS_NODIST)
 # Strict or non-strict aliasing flags used to compile dtoa.c, see above
 CFLAGS_ALIASING=@CFLAGS_ALIASING@
 
@@ -147,7 +155,7 @@ CONFINCLUDEPY=      $(CONFINCLUDEDIR)/python$(LDVERSION)
 SHLIB_SUFFIX=  @SHLIB_SUFFIX@
 EXT_SUFFIX=    @EXT_SUFFIX@
 LDSHARED=      @LDSHARED@ $(PY_LDFLAGS)
-BLDSHARED=     @BLDSHARED@ $(PY_LDFLAGS)
+BLDSHARED=     @BLDSHARED@ $(PY_CORE_LDFLAGS)
 LDCXXSHARED=   @LDCXXSHARED@
 DESTSHARED=    $(BINLIBDEST)/lib-dynload
 
@@ -496,7 +504,7 @@ profile-run-stamp:
        touch $@
 
 build_all_generate_profile:
-       $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS) $(PGO_PROF_GEN_FLAG)" LDFLAGS="$(LDFLAGS) $(PGO_PROF_GEN_FLAG)" LIBS="$(LIBS)"
+       $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS_NODIST) $(PGO_PROF_GEN_FLAG)" LDFLAGS_NODIST="$(LDFLAGS_NODIST) $(PGO_PROF_GEN_FLAG)" LIBS="$(LIBS)"
 
 run_profile_task:
        @ # FIXME: can't run for a cross build
@@ -510,7 +518,7 @@ build_all_merge_profile:
 profile-opt: profile-run-stamp
        @echo "Rebuilding with profile guided optimizations:"
        -rm -f profile-clean-stamp
-       $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS) $(PGO_PROF_USE_FLAG)" LDFLAGS="$(LDFLAGS)"
+       $(MAKE) @DEF_MAKE_RULE@ CFLAGS_NODIST="$(CFLAGS_NODIST) $(PGO_PROF_USE_FLAG)" LDFLAGS_NODIST="$(LDFLAGS_NODIST)"
 
 # Compile and run with gcov
 .PHONY=coverage coverage-lcov coverage-report
@@ -567,7 +575,7 @@ clinic: check-clean-src $(srcdir)/Modules/_blake2/blake2s_impl.c
 
 # Build the interpreter
 $(BUILDPYTHON):        Programs/python.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY)
-       $(LINKCC) $(PY_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST)
+       $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST)
 
 platform: $(BUILDPYTHON) pybuilddir.txt
        $(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import sys ; from sysconfig import get_platform ; print("%s-%d.%d" % (get_platform(), *sys.version_info[:2]))' >platform
@@ -632,7 +640,7 @@ libpython3.so:      libpython$(LDVERSION).so
        $(BLDSHARED) $(NO_AS_NEEDED) -o $@ -Wl,-h$@ $^
 
 libpython$(LDVERSION).dylib: $(LIBRARY_OBJS)
-        $(CC) -dynamiclib -Wl,-single_module $(PY_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(prefix)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(SHLIBS) $(LIBC) $(LIBM) $(LDLAST); \
+        $(CC) -dynamiclib -Wl,-single_module $(PY_CORE_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(prefix)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(SHLIBS) $(LIBC) $(LIBM) $(LDLAST); \
 
 
 libpython$(VERSION).sl: $(LIBRARY_OBJS)
@@ -657,7 +665,7 @@ $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK): \
                $(LIBRARY) \
                $(RESSRCDIR)/Info.plist
        $(INSTALL) -d -m $(DIRMODE) $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)
-       $(CC) -o $(LDLIBRARY) $(PY_LDFLAGS) -dynamiclib \
+       $(CC) -o $(LDLIBRARY) $(PY_CORE_LDFLAGS) -dynamiclib \
                -all_load $(LIBRARY) -Wl,-single_module \
                -install_name $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK) \
                -compatibility_version $(VERSION) \
@@ -708,7 +716,7 @@ Modules/Setup: $(srcdir)/Modules/Setup.dist
        fi
 
 Programs/_testembed: Programs/_testembed.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY)
-       $(LINKCC) $(PY_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/_testembed.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST)
+       $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/_testembed.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST)
 
 ############################################################################
 # Importlib
@@ -716,7 +724,7 @@ Programs/_testembed: Programs/_testembed.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY)
 Programs/_freeze_importlib.o: Programs/_freeze_importlib.c Makefile
 
 Programs/_freeze_importlib: Programs/_freeze_importlib.o $(LIBRARY_OBJS_OMIT_FROZEN)
-       $(LINKCC) $(PY_LDFLAGS) -o $@ Programs/_freeze_importlib.o $(LIBRARY_OBJS_OMIT_FROZEN) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST)
+       $(LINKCC) $(PY_CORE_LDFLAGS) -o $@ Programs/_freeze_importlib.o $(LIBRARY_OBJS_OMIT_FROZEN) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST)
 
 .PHONY: regen-importlib
 regen-importlib: Programs/_freeze_importlib
@@ -798,7 +806,7 @@ Python/sysmodule.o: $(srcdir)/Python/sysmodule.c Makefile
 $(IO_OBJS): $(IO_H)
 
 $(PGEN): $(PGENOBJS)
-               $(CC) $(OPT) $(PY_LDFLAGS) $(PGENOBJS) $(LIBS) -o $(PGEN)
+               $(CC) $(OPT) $(PY_CORE_LDFLAGS) $(PGENOBJS) $(LIBS) -o $(PGEN)
 
 .PHONY: regen-grammar
 regen-grammar: $(PGEN)
@@ -1442,11 +1450,21 @@ inclinstall:
                else    true; \
                fi; \
        done
+       @if test ! -d $(DESTDIR)$(INCLUDEPY)/internal; then \
+               echo "Creating directory $(DESTDIR)$(INCLUDEPY)/internal"; \
+               $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$(INCLUDEPY)/internal; \
+       else    true; \
+       fi
        @for i in $(srcdir)/Include/*.h; \
        do \
                echo $(INSTALL_DATA) $$i $(INCLUDEPY); \
                $(INSTALL_DATA) $$i $(DESTDIR)$(INCLUDEPY); \
        done
+       @for i in $(srcdir)/Include/internal/*.h; \
+       do \
+               echo $(INSTALL_DATA) $$i $(INCLUDEPY)/internal; \
+               $(INSTALL_DATA) $$i $(DESTDIR)$(INCLUDEPY)/internal; \
+       done
        $(INSTALL_DATA) pyconfig.h $(DESTDIR)$(CONFINCLUDEPY)/pyconfig.h
 
 # Install the library and miscellaneous stuff needed for extending/embedding
index 7cd8ebf..c35ef1c 100644 (file)
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1136,6 +1136,7 @@ Samuel Nicolary
 Jonathan Niehof
 Gustavo Niemeyer
 Oscar Nierstrasz
+Lysandros Nikolaou
 Hrvoje Nikšić
 Gregory Nofi
 Jesse Noller
index 944a44a..bce9073 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -2,6 +2,412 @@
 Python News
 +++++++++++
 
+What's New in Python 3.7.2 final?
+=================================
+
+*Release date: 2018-12-23*
+
+Library
+-------
+
+- bpo-31715: Associate ``.mjs`` file extension with
+  ``application/javascript`` MIME Type.
+
+Build
+-----
+
+- bpo-35499: ``make profile-opt`` no longer replaces ``CFLAGS_NODIST`` with
+  ``CFLAGS``. It now adds profile-guided optimization (PGO) flags to
+  ``CFLAGS_NODIST``: existing ``CFLAGS_NODIST`` flags are kept.
+
+- bpo-35257: Avoid leaking the linker flags from Link Time Optimizations
+  (LTO) into distutils when compiling C extensions.
+
+C API
+-----
+
+- bpo-35259: Conditionally declare :c:func:`Py_FinalizeEx()` (new in 3.6)
+  based on Py_LIMITED_API. Patch by Arthur Neufeld.
+
+
+What's New in Python 3.7.2 release candidate 1?
+===============================================
+
+*Release date: 2018-12-11*
+
+Security
+--------
+
+- bpo-34812: The :option:`-I` command line option (run Python in isolated
+  mode) is now also copied by the :mod:`multiprocessing` and
+  :mod:`distutils` modules when spawning child processes. Previously, only
+  :option:`-E` and :option:`-s` options (enabled by :option:`-I`) were
+  copied.
+
+- bpo-34791: The xml.sax and xml.dom.domreg no longer use environment
+  variables to override parser implementations when
+  sys.flags.ignore_environment is set by -E or -I arguments.
+
+Core and Builtins
+-----------------
+
+- bpo-35444: Fixed error handling in pickling methods when fail to look up
+  builtin "getattr".
+
+- bpo-35436: Fix various issues with memory allocation error handling.
+  Patch by Zackery Spytz.
+
+- bpo-35357: Internal attributes' names of unittest.mock._Call and
+  unittest.mock.MagicProxy (name, parent & from_kall) are now prefixed with
+  _mock_ in order to prevent clashes with widely used object attributes.
+  Fixed minor typo in test function name.
+
+- bpo-35372: Fixed the code page decoder for input longer than 2 GiB
+  containing undecodable bytes.
+
+- bpo-35336: Fix PYTHONCOERCECLOCALE=1 environment variable: only coerce the
+  C locale if the LC_CTYPE locale is "C".
+
+- bpo-33954: For :meth:`str.format`, :meth:`float.__format__` and
+  :meth:`complex.__format__` methods for non-ASCII decimal point when using
+  the "n" formatter.
+
+- bpo-35269: Fix a possible segfault involving a newly-created coroutine.
+  Patch by Zackery Spytz.
+
+- bpo-35214: Fixed an out of bounds memory access when parsing a truncated
+  unicode escape sequence at the end of a string such as ``'\N'``.  It would
+  read one byte beyond the end of the memory allocation.
+
+- bpo-35214: The interpreter and extension modules have had annotations
+  added so that they work properly under clang's Memory Sanitizer.  A new
+  configure flag --with-memory-sanitizer has been added to make test builds
+  of this nature easier to perform.
+
+- bpo-35193: Fix an off by one error in the bytecode peephole optimizer
+  where it could read bytes beyond the end of bounds of an array when
+  removing unreachable code. This bug was present in every release of Python
+  3.6 and 3.7 until now.
+
+- bpo-29341: Clarify in the docstrings of :mod:`os` methods that path-like
+  objects are also accepted as input parameters.
+
+- bpo-35050: :mod:`socket`: Fix off-by-one bug in length check for
+  ``AF_ALG`` name and type.
+
+- bpo-34974: :class:`bytes` and :class:`bytearray` constructors no longer
+  convert unexpected exceptions (e.g. :exc:`MemoryError` and
+  :exc:`KeyboardInterrupt`) to :exc:`TypeError`.
+
+- bpo-34973: Fixed crash in :func:`bytes` when the :class:`list` argument is
+  mutated while it is iterated.
+
+- bpo-34824: Fix a possible null pointer dereference in Modules/_ssl.c.
+  Patch by Zackery Spytz.
+
+- bpo-1621: Do not assume signed integer overflow behavior (C undefined
+  behavior) when performing set hash table resizing.
+
+Library
+-------
+
+- bpo-35052: Fix xml.dom.minidom cloneNode() on a document with an entity:
+  pass the correct arguments to the user data handler of an entity.
+
+- bpo-35330: When a :class:`Mock` instance was used to wrap an object, if
+  `side_effect` is used in one of the mocks of it methods, don't call the
+  original implementation and return the result of using the side effect the
+  same way that it is done with return_value.
+
+- bpo-34172: Revert the fix for this issue previously released in 3.7.1
+  pending further investigation: Fix a reference issue inside
+  multiprocessing.Pool that caused the pool to remain alive if it was
+  deleted without being closed or terminated explicitly.
+
+- bpo-10496: :func:`posixpath.expanduser` now returns the input *path*
+  unchanged if the ``HOME`` environment variable is not set and the current
+  user has no home directory (if the current user identifier doesn't exist
+  in the password database). This change fix the :mod:`site` module if the
+  current user doesn't exist in the password database (if the user has no
+  home directory).
+
+- bpo-35310: Fix a bug in :func:`select.select` where, in some cases, the
+  file descriptor sequences were returned unmodified after a signal
+  interruption, even though the file descriptors might not be ready yet.
+  :func:`select.select` will now always return empty lists if a timeout has
+  occurred.  Patch by Oran Avraham.
+
+- bpo-35380: Enable TCP_NODELAY on Windows for proactor asyncio event loop.
+
+- bpo-35341: Add generic version of ``collections.OrderedDict`` to the
+  ``typing`` module. Patch by Ismo Toijala.
+
+- bpo-35371: Fixed possible crash in ``os.utime()`` on Windows when pass
+  incorrect arguments.
+
+- bpo-27903: Fix ``ResourceWarning`` in :func:`platform.dist` on SuSE and
+  Caldera OpenLinux. Patch by Ville Skyttä.
+
+- bpo-35308: Fix regression in ``webbrowser`` where default browsers may be
+  preferred over browsers in the ``BROWSER`` environment variable.
+
+- bpo-28604: :func:`locale.localeconv` now sets temporarily the ``LC_CTYPE``
+  locale to the ``LC_MONETARY`` locale if the two locales are different and
+  monetary strings are non-ASCII. This temporary change affects other
+  threads.
+
+- bpo-35277: Update ensurepip to install pip 18.1 and setuptools 40.6.2.
+
+- bpo-35226: Recursively check arguments when testing for equality of
+  :class:`unittest.mock.call` objects and add note that tracking of
+  parameters used to create ancestors of mocks in ``mock_calls`` is not
+  possible.
+
+- bpo-29564: The warnings module now suggests to enable tracemalloc if the
+  source is specified, the tracemalloc module is available, but tracemalloc
+  is not tracing memory allocations.
+
+- bpo-35189: Modify the following fnctl function to retry if interrupted by
+  a signal (EINTR): flock, lockf, fnctl
+
+- bpo-35062: Fix incorrect parsing of
+  :class:`_io.IncrementalNewlineDecoder`'s *translate* argument.
+
+- bpo-35079: Improve difflib.SequenceManager.get_matching_blocks doc by
+  adding 'non-overlapping' and changing '!=' to '<'.
+
+- bpo-35017: :meth:`socketserver.BaseServer.serve_forever` now exits
+  immediately if it's :meth:`~socketserver.BaseServer.shutdown` method is
+  called while it is polling for new events.
+
+- bpo-31047: Fix ``ntpath.abspath`` regression where it didn't remove a
+  trailing separator on Windows. Patch by Tim Graham.
+
+- bpo-34794: Fixed a leak in Tkinter when pass the Python wrapper around
+  Tcl_Obj back to Tcl/Tk.
+
+- bpo-35008: Fixed references leaks when call the ``__setstate__()`` method
+  of :class:`xml.etree.ElementTree.Element` in the C implementation for
+  already initialized element.
+
+- bpo-23420: Verify the value for the parameter '-s' of the cProfile CLI.
+  Patch by Robert Kuska
+
+- bpo-33947: dataclasses now handle recursive reprs without raising
+  RecursionError.
+
+- bpo-16965: The :term:`2to3` :2to3fixer:`execfile` fixer now opens the file
+  with mode ``'rb'``.  Patch by Zackery Spytz.
+
+- bpo-34966: :mod:`pydoc` now supports aliases not only to methods defined
+  in the end class, but also to inherited methods.  The docstring is not
+  duplicated for aliases.
+
+- bpo-34941: Methods ``find()``, ``findtext()`` and ``findall()`` of the
+  ``Element`` class in the :mod:`xml.etree.ElementTree` module are now able
+  to find children which are instances of ``Element`` subclasses.
+
+- bpo-34936: Fix ``TclError`` in ``tkinter.Spinbox.selection_element()``.
+  Patch by Juliette Monsel.
+
+- bpo-34866: Adding ``max_num_fields`` to ``cgi.FieldStorage`` to make DOS
+  attacks harder by limiting the number of ``MiniFieldStorage`` objects
+  created by ``FieldStorage``.
+
+- bpo-34022: The :envvar:`SOURCE_DATE_EPOCH` environment variable no longer
+  overrides the value of the *invalidation_mode* argument to
+  :func:`py_compile.compile`, and determines its default value instead.
+
+- bpo-34738: ZIP files created by :mod:`distutils` will now include entries
+  for directories.
+
+- bpo-31177: Fix bug that prevented using :meth:`reset_mock
+  <unittest.mock.Mock.reset_mock>` on mock instances with deleted attributes
+
+- bpo-34536: `Enum._missing_`:  raise `ValueError` if None returned and
+  `TypeError` if non-member is returned.
+
+- bpo-34604: Fix possible mojibake in the error message of `pwd.getpwnam`
+  and `grp.getgrnam` using string representation because of invisible
+  characters or trailing whitespaces. Patch by William Grzybowski.
+
+- bpo-34574: OrderedDict iterators are not exhausted during pickling
+  anymore. Patch by Sergey Fedoseev.
+
+- bpo-34052: :meth:`sqlite3.Connection.create_aggregate`,
+  :meth:`sqlite3.Connection.create_function`,
+  :meth:`sqlite3.Connection.set_authorizer`,
+  :meth:`sqlite3.Connection.set_progress_handler` methods raises TypeError
+  when unhashable objects are passed as callable. These methods now don't
+  pass such objects to SQLite API. Previous behavior could lead to
+  segfaults. Patch by Sergey Fedoseev.
+
+- bpo-29877: compileall: import ProcessPoolExecutor only when needed,
+  preventing hangs on low resource platforms
+
+- bpo-22005: Implemented unpickling instances of
+  :class:`~datetime.datetime`, :class:`~datetime.date` and
+  :class:`~datetime.time` pickled by Python 2. ``encoding='latin1'`` should
+  be used for successful decoding.
+
+Documentation
+-------------
+
+- bpo-35089: Remove mention of ``typing.io`` and ``typing.re``. Their types
+  should be imported from ``typing`` directly.
+
+- bpo-35038: Fix the documentation about an unexisting `f_restricted`
+  attribute in the frame object. Patch by Stéphane Wirtel
+
+- bpo-35044: Fix the documentation with the role ``exc`` for the
+  appropriated exception. Patch by Stéphane Wirtel
+
+- bpo-35035: Rename documentation for :mod:`email.utils` to
+  ``email.utils.rst``.
+
+- bpo-34967: Use app.add_object_type() instead of the deprecated Sphinx
+  function app.description_unit()
+
+- bpo-11233: Create availability directive for documentation.  Original
+  patch by Georg Brandl.
+
+- bpo-33594: Document ``getargspec``, ``from_function`` and ``from_builtin``
+  as deprecated in their respective docstring, and include version since
+  deprecation in DeprecationWarning message.
+
+- bpo-32613: Update the faq/windows.html to use the py command from PEP 397
+  instead of python.
+
+Tests
+-----
+
+- bpo-33725: test_multiprocessing_fork may crash on recent versions of
+  macOS.  Until the issue is resolved, skip the test on macOS.
+
+- bpo-35352: Modify test_asyncio to use the certificate set from the test
+  directory.
+
+- bpo-35317: Fix ``mktime()`` overflow error in ``test_email``: run
+  ``test_localtime_daylight_true_dst_true()`` and
+  ``test_localtime_daylight_false_dst_true()`` with a specific timezone.
+
+- bpo-21263: After several reports that test_gdb does not work properly on
+  macOS and since gdb is not shipped by default anymore, test_gdb is now
+  skipped on macOS when LLVM Clang has been used to compile Python. Patch by
+  Lysandros Nikolaou
+
+- bpo-34279: regrtest issue a warning when no tests have been executed in a
+  particular test file. Also, a new final result state is issued if no test
+  have been executed across all test files. Patch by Pablo Galindo.
+
+Build
+-----
+
+- bpo-35296: The Windows installer (MSI) now also install internal header
+  files (``Include/internal/`` subdirectory).
+
+- bpo-35351: When building Python with clang and LTO, LTO flags are no
+  longer passed into CFLAGS to build third-party C extensions through
+  distutils.
+
+- bpo-35139: Fix a compiler error when statically linking `pyexpat` in
+  `Modules/Setup`.
+
+- bpo-35011: Restores the use of pyexpatns.h to isolate our embedded copy of
+  the expat C library so that its symbols do not conflict at link or dynamic
+  loading time with an embedding application or other extension modules with
+  their own version of libexpat.
+
+- bpo-28015: Have --with-lto works correctly with clang.
+
+- bpo-33015: Fix an undefined behaviour in the pthread implementation of
+  :c:func:`PyThread_start_new_thread`: add a function wrapper to always
+  return ``NULL``.
+
+Windows
+-------
+
+- bpo-35401: Updates Windows build to OpenSSL 1.1.0j
+
+- bpo-34977: venv on Windows will now use a python.exe redirector rather
+  than copying the actual binaries from the base environment.
+
+- bpo-34977: Adds support for building a Windows App Store package
+
+- bpo-35067: Remove _distutils_findvs module and use vswhere.exe instead.
+
+- bpo-34532: Fixes exit code of list version arguments for py.exe.
+
+- bpo-32890: Fix usage of GetLastError() instead of errno in os.execve() and
+  os.truncate().
+
+macOS
+-----
+
+- bpo-35402: Update macOS installer to use Tcl/Tk 8.6.9.1.
+
+- bpo-35401: Update macOS installer to use OpenSSL 1.1.0j.
+
+- bpo-35025: Properly guard the use of the ``CLOCK_GETTIME`` et al. macros
+  in ``timemodule`` on macOS.
+
+- bpo-24658: On macOS, fix reading from and writing into a file with a size
+  larger than 2 GiB.
+
+IDLE
+----
+
+- bpo-35213: Where appropriate, use 'macOS' in idlelib.
+
+- bpo-34864: On macOS, warn if the system preference "Prefer tabs when
+  opening documents" is set to "Always".
+
+- bpo-34864: Document two IDLE on MacOS issues. The System Preferences Dock
+  "prefer tabs always" setting disables some IDLE features.  Menus are a bit
+  different than as described for Windows and Linux.
+
+- bpo-35202: Remove unused imports from lib/idlelib
+
+- bpo-33000: Document that IDLE's shell has no line limit. A program that
+  runs indefinitely can overfill memory.
+
+- bpo-23220: Explain how IDLE's Shell displays output.
+
+- bpo-35099: Improve the doc about IDLE running user code.   The section is
+  renamed from "IDLE -- console differences" is renamed "Running user code".
+  It mostly covers the implications of using custom sys.stdxxx objects.
+
+- bpo-35097: Add IDLE doc subsection explaining editor windows. Topics
+  include opening, title and status bar, .py* extension, and running.
+
+- bpo-35093: Document the IDLE document viewer in the IDLE doc. Add a
+  paragraph in "Help and preferences", "Help sources" subsection.
+
+- bpo-35088: Update idlelib.help.copy_string docstring. We now use git and
+  backporting instead of hg and forward merging.
+
+- bpo-35087: Update idlelib help files for the current doc build. The main
+  change is the elimination of chapter-section numbers.
+
+Tools/Demos
+-----------
+
+- bpo-34989: python-gdb.py now handles errors on computing the line number
+  of a Python frame.
+
+C API
+-----
+
+- bpo-35322: Fix memory leak in :c:func:`PyUnicode_EncodeLocale` and
+  :c:func:`PyUnicode_EncodeFSDefault` on error handling.
+
+- bpo-35296: ``make install`` now also installs the internal API:
+  ``Include/internal/*.h`` header files.
+
+- bpo-34725: Adds _Py_SetProgramFullPath so embedders may override
+  sys.executable
+
+
 What's New in Python 3.7.1 final?
 =================================
 
@@ -570,7 +976,7 @@ IDLE
 - bpo-34120: Fix unresponsiveness after closing certain windows and dialogs.
 
 - bpo-33975: Avoid small type when running htests. Since part of the purpose
-  of human- viewed tests is to determine that widgets look right, it is
+  of human-viewed tests is to determine that widgets look right, it is
   important that they look the same for testing as when running IDLE.
 
 - bpo-33905: Add test for idlelib.stackview.StackBrowser.
@@ -10854,7 +11260,7 @@ Security
   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
+  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()``.
 
@@ -10949,7 +11355,7 @@ Library
 - bpo-29169: Update zlib to 1.2.11.
 
 - bpo-30879: os.listdir() and os.scandir() now emit bytes names when called
-  with bytes- like argument.
+  with bytes-like argument.
 
 - bpo-30746: Prohibited the '=' character in environment variable names in
   ``os.putenv()`` and ``os.spawn*()``.
@@ -11036,8 +11442,8 @@ Library
   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.
 
@@ -11220,7 +11626,7 @@ Build
 
 - 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
@@ -11291,7 +11697,7 @@ Core and Builtins
   when decode astral characters.  Patch by Xiang Zhang.
 
 - bpo-19398: Extra slash no longer added to sys.path components in case of
-  empty compile- time PYTHONPATH components.
+  empty compile-time PYTHONPATH components.
 
 - bpo-28426: Fixed potential crash in PyUnicode_AsDecodedObject() in debug
   build.
@@ -11375,7 +11781,7 @@ Core and Builtins
 
 - 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.
 
 - bpo-27083: Respect the PYTHONCASEOK environment variable under Windows.
 
@@ -11614,10 +12020,10 @@ Library
 - 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).
 
 - bpo-27539: Fix unnormalised ``Fraction.__pow__`` result in the case of
   negative exponent and negative base.
@@ -11921,8 +12327,8 @@ Build
 
 - 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.
+  --with-optimizations is enabled.  Also improve our ability to find the
+  llvm-profdata tool on MacOS and some Linuxes.
 
 - bpo-26307: The profile-opt build now applies PGO to the built-in modules.
 
@@ -11940,8 +12346,8 @@ Build
   Hsuan Yen.
 
 - bpo-27641: The configure script now inserts comments into the makefile to
-  prevent the pgen and _freeze_importlib executables from being cross-
-  compiled.
+  prevent the pgen and _freeze_importlib executables from being
+  cross-compiled.
 
 - bpo-26662: Set PYTHON_FOR_GEN in configure as the Python program to be
   used for file generation during the build.
@@ -12186,9 +12592,9 @@ Library
 
 - 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.
+  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.
 
 - bpo-26892: Honor debuglevel flag in urllib.request.HTTPHandler. Patch
   contributed by Chi Hsuan Yen.
@@ -12641,7 +13047,7 @@ Build
   de Gaye.
 
 - bpo-22359: Disable the rules for running _freeze_importlib and pgen when
-  cross- compiling.  The output of these programs is normally saved with the
+  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.
 
@@ -12810,7 +13216,7 @@ Core and Builtins
 - 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.
+  need such high-quality entropy.
 
 - bpo-25182: The stdprinter (used as sys.stderr before the io module is
   imported at startup) now uses the backslashreplace error handler.
@@ -13118,12 +13524,12 @@ IDLE
 - bpo-25173: Associate tkinter messageboxes with a specific widget. For Mac
   OSX, make them a 'sheet'.  Patch by Mark Roseman.
 
-- 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.
+- 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.
 
 - bpo-25225: Condense and rewrite Idle doc section on text colors.
 
@@ -13501,7 +13907,7 @@ Library
   inside a data segment.
 
 - bpo-15014: SMTP.auth() and SMTP.login() now support RFC 4954's optional
-  initial- response argument to the SMTP AUTH command.
+  initial-response argument to the SMTP AUTH command.
 
 - bpo-24669: Fix inspect.getsource() for 'async def' functions. Patch by Kai
   Groner.
@@ -13565,7 +13971,7 @@ Library
   called.  Based on patch by Martin Panter.
 
 - bpo-20387: Restore semantic round-trip correctness in tokenize/untokenize
-  for tab- indented blocks.
+  for tab-indented blocks.
 
 - bpo-24456: Fixed possible buffer over-read in adpcm2lin() and lin2adpcm()
   functions of the audioop module.
@@ -14203,7 +14609,7 @@ Library
 -------
 
 - bpo-14260: The groupindex attribute of regular expression pattern object
-  now is non- modifiable mapping.
+  now is non-modifiable mapping.
 
 - bpo-23792: Ignore KeyboardInterrupt when the pydoc pager is active. This
   mimics the behavior of the standard unix pagers, and prevents pipepager
@@ -15338,7 +15744,7 @@ Library
 - bpo-5411: Added support for the "xztar" format in the shutil module.
 
 - bpo-21121: Don't force 3rd party C extensions to be built with
-  -Werror=declaration- after-statement.
+  -Werror=declaration-after-statement.
 
 - bpo-21975: Fixed crash when using uninitialized sqlite3.Row (in particular
   when unpickling pickled sqlite3.Row).  sqlite3.Row is now initialized in
@@ -15874,7 +16280,7 @@ Library
 - bpo-20378: Improve repr of inspect.Signature and inspect.Parameter.
 
 - bpo-20816: Fix inspect.getcallargs() to raise correct TypeError for
-  missing keyword- only arguments. Patch by Jeremiah Lowin.
+  missing keyword-only arguments. Patch by Jeremiah Lowin.
 
 - bpo-20817: Fix inspect.getcallargs() to fail correctly if more than 3
   arguments are missing. Patch by Jeremiah Lowin.
@@ -16073,7 +16479,7 @@ Build
 - bpo-21811: Anticipated fixes to support OS X versions > 10.9.
 
 - bpo-21166: Prevent possible segfaults and other random failures of python
-  --generate- posix-vars in pybuilddir.txt build target.
+  --generate-posix-vars in pybuilddir.txt build target.
 
 - bpo-18096: Fix library order returned by python-config.
 
index f0eff5d..8cc6bf0 100644 (file)
@@ -340,7 +340,7 @@ _symtable symtablemodule.c
 # Interface to the Expat XML parser
 # More information on Expat can be found at www.libexpat.org.
 #
-#pyexpat expat/xmlparse.c expat/xmlrole.c expat/xmltok.c pyexpat.c -I$(srcdir)/Modules/expat -DHAVE_EXPAT_CONFIG_H -DUSE_PYEXPAT_CAPI
+#pyexpat expat/xmlparse.c expat/xmlrole.c expat/xmltok.c pyexpat.c -I$(srcdir)/Modules/expat -DHAVE_EXPAT_CONFIG_H -DXML_POOR_ENTROPY=1 -DUSE_PYEXPAT_CAPI
 
 # Hye-Shik Chang's CJKCodecs
 
index 9de199f..36c1757 100644 (file)
@@ -728,6 +728,10 @@ subclasscheck_check_registry(_abc_data *impl, PyObject *subclass,
     // Weakref callback may remove entry from set.
     // So we take snapshot of registry first.
     PyObject **copy = PyMem_Malloc(sizeof(PyObject*) * registry_size);
+    if (copy == NULL) {
+        PyErr_NoMemory();
+        return -1;
+    }
     PyObject *key;
     Py_ssize_t pos = 0;
     Py_hash_t hash;
index 809879a..5816a67 100644 (file)
@@ -1094,7 +1094,7 @@ _asyncio_Future_get_loop_impl(FutureObj *self)
 }
 
 static PyObject *
-FutureObj_get_blocking(FutureObj *fut)
+FutureObj_get_blocking(FutureObj *fut, void *Py_UNUSED(ignored))
 {
     if (future_is_alive(fut) && fut->fut_blocking) {
         Py_RETURN_TRUE;
@@ -1105,7 +1105,7 @@ FutureObj_get_blocking(FutureObj *fut)
 }
 
 static int
-FutureObj_set_blocking(FutureObj *fut, PyObject *val)
+FutureObj_set_blocking(FutureObj *fut, PyObject *val, void *Py_UNUSED(ignored))
 {
     if (future_ensure_alive(fut)) {
         return -1;
@@ -1120,7 +1120,7 @@ FutureObj_set_blocking(FutureObj *fut, PyObject *val)
 }
 
 static PyObject *
-FutureObj_get_log_traceback(FutureObj *fut)
+FutureObj_get_log_traceback(FutureObj *fut, void *Py_UNUSED(ignored))
 {
     ENSURE_FUTURE_ALIVE(fut)
     if (fut->fut_log_tb) {
@@ -1132,7 +1132,7 @@ FutureObj_get_log_traceback(FutureObj *fut)
 }
 
 static int
-FutureObj_set_log_traceback(FutureObj *fut, PyObject *val)
+FutureObj_set_log_traceback(FutureObj *fut, PyObject *val, void *Py_UNUSED(ignored))
 {
     int is_true = PyObject_IsTrue(val);
     if (is_true < 0) {
@@ -1148,7 +1148,7 @@ FutureObj_set_log_traceback(FutureObj *fut, PyObject *val)
 }
 
 static PyObject *
-FutureObj_get_loop(FutureObj *fut)
+FutureObj_get_loop(FutureObj *fut, void *Py_UNUSED(ignored))
 {
     if (!future_is_alive(fut)) {
         Py_RETURN_NONE;
@@ -1158,7 +1158,7 @@ FutureObj_get_loop(FutureObj *fut)
 }
 
 static PyObject *
-FutureObj_get_callbacks(FutureObj *fut)
+FutureObj_get_callbacks(FutureObj *fut, void *Py_UNUSED(ignored))
 {
     Py_ssize_t i;
 
@@ -1210,7 +1210,7 @@ FutureObj_get_callbacks(FutureObj *fut)
 }
 
 static PyObject *
-FutureObj_get_result(FutureObj *fut)
+FutureObj_get_result(FutureObj *fut, void *Py_UNUSED(ignored))
 {
     ENSURE_FUTURE_ALIVE(fut)
     if (fut->fut_result == NULL) {
@@ -1221,7 +1221,7 @@ FutureObj_get_result(FutureObj *fut)
 }
 
 static PyObject *
-FutureObj_get_exception(FutureObj *fut)
+FutureObj_get_exception(FutureObj *fut, void *Py_UNUSED(ignored))
 {
     ENSURE_FUTURE_ALIVE(fut)
     if (fut->fut_exception == NULL) {
@@ -1232,7 +1232,7 @@ FutureObj_get_exception(FutureObj *fut)
 }
 
 static PyObject *
-FutureObj_get_source_traceback(FutureObj *fut)
+FutureObj_get_source_traceback(FutureObj *fut, void *Py_UNUSED(ignored))
 {
     if (!future_is_alive(fut) || fut->fut_source_tb == NULL) {
         Py_RETURN_NONE;
@@ -1242,7 +1242,7 @@ FutureObj_get_source_traceback(FutureObj *fut)
 }
 
 static PyObject *
-FutureObj_get_state(FutureObj *fut)
+FutureObj_get_state(FutureObj *fut, void *Py_UNUSED(ignored))
 {
     _Py_IDENTIFIER(PENDING);
     _Py_IDENTIFIER(CANCELLED);
@@ -1723,7 +1723,7 @@ TaskStepMethWrapper_traverse(TaskStepMethWrapper *o,
 }
 
 static PyObject *
-TaskStepMethWrapper_get___self__(TaskStepMethWrapper *o)
+TaskStepMethWrapper_get___self__(TaskStepMethWrapper *o, void *Py_UNUSED(ignored))
 {
     if (o->sw_task) {
         Py_INCREF(o->sw_task);
@@ -1995,7 +1995,7 @@ TaskObj_traverse(TaskObj *task, visitproc visit, void *arg)
 }
 
 static PyObject *
-TaskObj_get_log_destroy_pending(TaskObj *task)
+TaskObj_get_log_destroy_pending(TaskObj *task, void *Py_UNUSED(ignored))
 {
     if (task->task_log_destroy_pending) {
         Py_RETURN_TRUE;
@@ -2006,7 +2006,7 @@ TaskObj_get_log_destroy_pending(TaskObj *task)
 }
 
 static int
-TaskObj_set_log_destroy_pending(TaskObj *task, PyObject *val)
+TaskObj_set_log_destroy_pending(TaskObj *task, PyObject *val, void *Py_UNUSED(ignored))
 {
     int is_true = PyObject_IsTrue(val);
     if (is_true < 0) {
@@ -2017,7 +2017,7 @@ TaskObj_set_log_destroy_pending(TaskObj *task, PyObject *val)
 }
 
 static PyObject *
-TaskObj_get_must_cancel(TaskObj *task)
+TaskObj_get_must_cancel(TaskObj *task, void *Py_UNUSED(ignored))
 {
     if (task->task_must_cancel) {
         Py_RETURN_TRUE;
@@ -2028,7 +2028,7 @@ TaskObj_get_must_cancel(TaskObj *task)
 }
 
 static PyObject *
-TaskObj_get_coro(TaskObj *task)
+TaskObj_get_coro(TaskObj *task, void *Py_UNUSED(ignored))
 {
     if (task->task_coro) {
         Py_INCREF(task->task_coro);
@@ -2039,7 +2039,7 @@ TaskObj_get_coro(TaskObj *task)
 }
 
 static PyObject *
-TaskObj_get_fut_waiter(TaskObj *task)
+TaskObj_get_fut_waiter(TaskObj *task, void *Py_UNUSED(ignored))
 {
     if (task->task_fut_waiter) {
         Py_INCREF(task->task_fut_waiter);
index 3890b60..f0d9588 100644 (file)
@@ -277,11 +277,11 @@ BZ2_Malloc(void* ctx, int items, int size)
 {
     if (items < 0 || size < 0)
         return NULL;
-    if ((size_t)items > (size_t)PY_SSIZE_T_MAX / (size_t)size)
+    if (size != 0 && (size_t)items > (size_t)PY_SSIZE_T_MAX / (size_t)size)
         return NULL;
     /* PyMem_Malloc() cannot be used: compress() and decompress()
        release the GIL */
-    return PyMem_RawMalloc(items * size);
+    return PyMem_RawMalloc((size_t)items * (size_t)size);
 }
 
 static void
index cfd8905..decd6ae 100644 (file)
@@ -1344,7 +1344,7 @@ deque_traverse(dequeobject *deque, visitproc visit, void *arg)
 }
 
 static PyObject *
-deque_reduce(dequeobject *deque)
+deque_reduce(dequeobject *deque, PyObject *Py_UNUSED(ignored))
 {
     PyObject *dict, *it;
     _Py_IDENTIFIER(__dict__);
@@ -1542,7 +1542,7 @@ deque_bool(dequeobject *deque)
 }
 
 static PyObject *
-deque_get_maxlen(dequeobject *deque)
+deque_get_maxlen(dequeobject *deque, void *Py_UNUSED(ignored))
 {
     if (deque->maxlen < 0)
         Py_RETURN_NONE;
index 180f3be..dd0b3c8 100644 (file)
@@ -159,31 +159,31 @@ get_nullchar_as_None(Py_UCS4 c)
 }
 
 static PyObject *
-Dialect_get_lineterminator(DialectObj *self)
+Dialect_get_lineterminator(DialectObj *self, void *Py_UNUSED(ignored))
 {
     return get_string(self->lineterminator);
 }
 
 static PyObject *
-Dialect_get_delimiter(DialectObj *self)
+Dialect_get_delimiter(DialectObj *self, void *Py_UNUSED(ignored))
 {
     return get_nullchar_as_None(self->delimiter);
 }
 
 static PyObject *
-Dialect_get_escapechar(DialectObj *self)
+Dialect_get_escapechar(DialectObj *self, void *Py_UNUSED(ignored))
 {
     return get_nullchar_as_None(self->escapechar);
 }
 
 static PyObject *
-Dialect_get_quotechar(DialectObj *self)
+Dialect_get_quotechar(DialectObj *self, void *Py_UNUSED(ignored))
 {
     return get_nullchar_as_None(self->quotechar);
 }
 
 static PyObject *
-Dialect_get_quoting(DialectObj *self)
+Dialect_get_quoting(DialectObj *self, void *Py_UNUSED(ignored))
 {
     return PyLong_FromLong(self->quoting);
 }
index 3ae6348..c5fc811 100644 (file)
@@ -305,8 +305,10 @@ _ctypes_alloc_format_string_for_type(char code, int big_endian)
     }
 
     result = PyMem_Malloc(3);
-    if (result == NULL)
+    if (result == NULL) {
+        PyErr_NoMemory();
         return NULL;
+    }
 
     result[0] = big_endian ? '>' : '<';
     result[1] = pep_code;
@@ -366,8 +368,10 @@ _ctypes_alloc_format_string_with_shape(int ndim, const Py_ssize_t *shape,
     if (prefix)
         prefix_len += strlen(prefix);
     new_prefix = PyMem_Malloc(prefix_len);
-    if (new_prefix == NULL)
+    if (new_prefix == NULL) {
+        PyErr_NoMemory();
         return NULL;
+    }
     new_prefix[0] = '\0';
     if (prefix)
         strcpy(new_prefix, prefix);
@@ -1161,7 +1165,7 @@ PyTypeObject PyCPointerType_Type = {
 */
 
 static int
-CharArray_set_raw(CDataObject *self, PyObject *value)
+CharArray_set_raw(CDataObject *self, PyObject *value, void *Py_UNUSED(ignored))
 {
     char *ptr;
     Py_ssize_t size;
@@ -1187,13 +1191,13 @@ CharArray_set_raw(CDataObject *self, PyObject *value)
 }
 
 static PyObject *
-CharArray_get_raw(CDataObject *self)
+CharArray_get_raw(CDataObject *self, void *Py_UNUSED(ignored))
 {
     return PyBytes_FromStringAndSize(self->b_ptr, self->b_size);
 }
 
 static PyObject *
-CharArray_get_value(CDataObject *self)
+CharArray_get_value(CDataObject *self, void *Py_UNUSED(ignored))
 {
     Py_ssize_t i;
     char *ptr = self->b_ptr;
@@ -1204,7 +1208,7 @@ CharArray_get_value(CDataObject *self)
 }
 
 static int
-CharArray_set_value(CDataObject *self, PyObject *value)
+CharArray_set_value(CDataObject *self, PyObject *value, void *Py_UNUSED(ignored))
 {
     char *ptr;
     Py_ssize_t size;
@@ -1249,7 +1253,7 @@ static PyGetSetDef CharArray_getsets[] = {
 
 #ifdef CTYPES_UNICODE
 static PyObject *
-WCharArray_get_value(CDataObject *self)
+WCharArray_get_value(CDataObject *self, void *Py_UNUSED(ignored))
 {
     Py_ssize_t i;
     wchar_t *ptr = (wchar_t *)self->b_ptr;
@@ -1260,7 +1264,7 @@ WCharArray_get_value(CDataObject *self)
 }
 
 static int
-WCharArray_set_value(CDataObject *self, PyObject *value)
+WCharArray_set_value(CDataObject *self, PyObject *value, void *Py_UNUSED(ignored))
 {
     Py_ssize_t result = 0;
     Py_UNICODE *wstr;
@@ -1851,6 +1855,10 @@ static PyObject *CreateSwappedType(PyTypeObject *type, PyObject *args, PyObject
 #else
         suffix = PyUnicode_InternFromString("_be");
 #endif
+    if (suffix == NULL) {
+        Py_DECREF(swapped_args);
+        return NULL;
+    }
 
     newname = PyUnicode_Concat(name, suffix);
     if (newname == NULL) {
@@ -3045,7 +3053,7 @@ GenericPyCData_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 */
 
 static int
-PyCFuncPtr_set_errcheck(PyCFuncPtrObject *self, PyObject *ob)
+PyCFuncPtr_set_errcheck(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ignored))
 {
     if (ob && !PyCallable_Check(ob)) {
         PyErr_SetString(PyExc_TypeError,
@@ -3058,7 +3066,7 @@ PyCFuncPtr_set_errcheck(PyCFuncPtrObject *self, PyObject *ob)
 }
 
 static PyObject *
-PyCFuncPtr_get_errcheck(PyCFuncPtrObject *self)
+PyCFuncPtr_get_errcheck(PyCFuncPtrObject *self, void *Py_UNUSED(ignored))
 {
     if (self->errcheck) {
         Py_INCREF(self->errcheck);
@@ -3068,7 +3076,7 @@ PyCFuncPtr_get_errcheck(PyCFuncPtrObject *self)
 }
 
 static int
-PyCFuncPtr_set_restype(PyCFuncPtrObject *self, PyObject *ob)
+PyCFuncPtr_set_restype(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ignored))
 {
     if (ob == NULL) {
         Py_CLEAR(self->restype);
@@ -3089,7 +3097,7 @@ PyCFuncPtr_set_restype(PyCFuncPtrObject *self, PyObject *ob)
 }
 
 static PyObject *
-PyCFuncPtr_get_restype(PyCFuncPtrObject *self)
+PyCFuncPtr_get_restype(PyCFuncPtrObject *self, void *Py_UNUSED(ignored))
 {
     StgDictObject *dict;
     if (self->restype) {
@@ -3107,7 +3115,7 @@ PyCFuncPtr_get_restype(PyCFuncPtrObject *self)
 }
 
 static int
-PyCFuncPtr_set_argtypes(PyCFuncPtrObject *self, PyObject *ob)
+PyCFuncPtr_set_argtypes(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ignored))
 {
     PyObject *converters;
 
@@ -3126,7 +3134,7 @@ PyCFuncPtr_set_argtypes(PyCFuncPtrObject *self, PyObject *ob)
 }
 
 static PyObject *
-PyCFuncPtr_get_argtypes(PyCFuncPtrObject *self)
+PyCFuncPtr_get_argtypes(PyCFuncPtrObject *self, void *Py_UNUSED(ignored))
 {
     StgDictObject *dict;
     if (self->argtypes) {
@@ -4670,7 +4678,7 @@ PyCArrayType_from_ctype(PyObject *itemtype, Py_ssize_t length)
 */
 
 static int
-Simple_set_value(CDataObject *self, PyObject *value)
+Simple_set_value(CDataObject *self, PyObject *value, void *Py_UNUSED(ignored))
 {
     PyObject *result;
     StgDictObject *dict = PyObject_stgdict((PyObject *)self);
@@ -4697,12 +4705,12 @@ Simple_init(CDataObject *self, PyObject *args, PyObject *kw)
     if (!PyArg_UnpackTuple(args, "__init__", 0, 1, &value))
         return -1;
     if (value)
-        return Simple_set_value(self, value);
+        return Simple_set_value(self, value, NULL);
     return 0;
 }
 
 static PyObject *
-Simple_get_value(CDataObject *self)
+Simple_get_value(CDataObject *self, void *Py_UNUSED(ignored))
 {
     StgDictObject *dict;
     dict = PyObject_stgdict((PyObject *)self);
@@ -4725,7 +4733,7 @@ Simple_from_outparm(PyObject *self, PyObject *args)
         return self;
     }
     /* call stgdict->getfunc */
-    return Simple_get_value((CDataObject *)self);
+    return Simple_get_value((CDataObject *)self, NULL);
 }
 
 static PyMethodDef Simple_methods[] = {
@@ -4762,7 +4770,7 @@ Simple_repr(CDataObject *self)
                                    Py_TYPE(self)->tp_name, self);
     }
 
-    val = Simple_get_value(self);
+    val = Simple_get_value(self, NULL);
     if (val == NULL)
         return NULL;
 
index d579291..ec9f443 100644 (file)
@@ -305,7 +305,6 @@ static CThunkObject* CThunkObject_new(Py_ssize_t nArgs)
 
     p = PyObject_GC_NewVar(CThunkObject, &PyCThunk_Type, nArgs);
     if (p == NULL) {
-        PyErr_NoMemory();
         return NULL;
     }
 
index ba154fe..ec596b4 100644 (file)
 #include <alloca.h>
 #endif
 
+#ifdef _Py_MEMORY_SANITIZER
+#include <sanitizer/msan_interface.h>
+#endif
+
 #if defined(_DEBUG) || defined(__MINGW32__)
 /* Don't use structured exception handling on Windows if this is defined.
    MingW, AFAIK, doesn't support it.
@@ -448,6 +452,12 @@ PyCArg_dealloc(PyCArgObject *self)
     PyObject_Del(self);
 }
 
+static int
+is_literal_char(unsigned char c)
+{
+    return c < 128 && _PyUnicode_IsPrintable(c) && c != '\\' && c != '\'';
+}
+
 static PyObject *
 PyCArg_repr(PyCArgObject *self)
 {
@@ -480,7 +490,7 @@ PyCArg_repr(PyCArgObject *self)
 #ifdef MS_WIN32
             "<cparam '%c' (%I64d)>",
 #else
-            "<cparam '%c' (%qd)>",
+            "<cparam '%c' (%lld)>",
 #endif
             self->tag, self->value.q);
         break;
@@ -494,8 +504,14 @@ PyCArg_repr(PyCArgObject *self)
         break;
 
     case 'c':
-        sprintf(buffer, "<cparam '%c' (%c)>",
-            self->tag, self->value.c);
+        if (is_literal_char((unsigned char)self->value.c)) {
+            sprintf(buffer, "<cparam '%c' ('%c')>",
+                self->tag, self->value.c);
+        }
+        else {
+            sprintf(buffer, "<cparam '%c' ('\\x%02x')>",
+                self->tag, (unsigned char)self->value.c);
+        }
         break;
 
 /* Hm, are these 'z' and 'Z' codes useful at all?
@@ -510,8 +526,14 @@ PyCArg_repr(PyCArgObject *self)
         break;
 
     default:
-        sprintf(buffer, "<cparam '%c' at %p>",
-            self->tag, self);
+        if (is_literal_char((unsigned char)self->tag)) {
+            sprintf(buffer, "<cparam '%c' at %p>",
+                (unsigned char)self->tag, self);
+        }
+        else {
+            sprintf(buffer, "<cparam 0x%02x at %p>",
+                (unsigned char)self->tag, self);
+        }
         break;
     }
     return PyUnicode_FromString(buffer);
@@ -1125,6 +1147,13 @@ PyObject *_ctypes_callproc(PPROC pProc,
     rtype = _ctypes_get_ffi_type(restype);
     resbuf = alloca(max(rtype->size, sizeof(ffi_arg)));
 
+#ifdef _Py_MEMORY_SANITIZER
+    /* ffi_call actually initializes resbuf, but from asm, which
+     * MemorySanitizer can't detect. Avoid false positives from MSan. */
+    if (resbuf != NULL) {
+        __msan_unpoison(resbuf, max(rtype->size, sizeof(ffi_arg)));
+    }
+#endif
     avalues = (void **)alloca(sizeof(void *) * argcount);
     atypes = (ffi_type **)alloca(sizeof(ffi_type *) * argcount);
     if (!resbuf || !avalues || !atypes) {
index 7936aef..931c5ce 100644 (file)
@@ -414,27 +414,27 @@ PyTypeObject PyCursesWindow_Type;
    PARSESTR - format string for argument parsing
 */
 
-#define Window_NoArgNoReturnFunction(X)                 \
-    static PyObject *PyCursesWindow_ ## X               \
-    (PyCursesWindowObject *self, PyObject *args)        \
+#define Window_NoArgNoReturnFunction(X)                         \
+    static PyObject *PyCursesWindow_ ## X                       \
+    (PyCursesWindowObject *self, PyObject *Py_UNUSED(ignored))  \
     { return PyCursesCheckERR(X(self->win), # X); }
 
 #define Window_NoArgTrueFalseFunction(X)                                \
     static PyObject * PyCursesWindow_ ## X                              \
-    (PyCursesWindowObject *self)                                        \
+    (PyCursesWindowObject *self, PyObject *Py_UNUSED(ignored))          \
     {                                                                   \
         if (X (self->win) == FALSE) { Py_RETURN_FALSE; } \
         else { Py_RETURN_TRUE; } }
 
 #define Window_NoArgNoReturnVoidFunction(X)                     \
     static PyObject * PyCursesWindow_ ## X                      \
-    (PyCursesWindowObject *self)                                \
+    (PyCursesWindowObject *self, PyObject *Py_UNUSED(ignored))  \
     {                                                           \
         X(self->win); Py_RETURN_NONE; }
 
 #define Window_NoArg2TupleReturnFunction(X, TYPE, ERGSTR)               \
     static PyObject * PyCursesWindow_ ## X                              \
-    (PyCursesWindowObject *self)                                        \
+    (PyCursesWindowObject *self, PyObject *Py_UNUSED(ignored))          \
     {                                                                   \
         TYPE arg1, arg2;                                                \
         X(self->win,arg1,arg2); return Py_BuildValue(ERGSTR, arg1, arg2); }
@@ -1960,7 +1960,7 @@ PyCursesWindow_get_encoding(PyCursesWindowObject *self, void *closure)
 }
 
 static int
-PyCursesWindow_set_encoding(PyCursesWindowObject *self, PyObject *value)
+PyCursesWindow_set_encoding(PyCursesWindowObject *self, PyObject *value, void *Py_UNUSED(ignored))
 {
     PyObject *ascii;
     char *encoding;
index 41b95b9..5afeeea 100644 (file)
@@ -667,8 +667,8 @@ set_date_fields(PyDateTime_Date *self, int y, int m, int d)
  * String parsing utilities and helper functions
  */
 
-static const char*
-parse_digits(const char* ptr, int* var, size_t num_digits)
+static const char *
+parse_digits(const char *ptr, int *var, size_t num_digits)
 {
     for (size_t i = 0; i < num_digits; ++i) {
         unsigned int tmp = (unsigned int)(*(ptr++) - '0');
@@ -682,15 +682,16 @@ parse_digits(const char* ptr, int* var, size_t num_digits)
     return ptr;
 }
 
-static int parse_isoformat_date(const char *dtstr,
-                                int* year, int *month, int* day) {
+static int
+parse_isoformat_date(const char *dtstr, int *year, int *month, int *day)
+{
     /* Parse the date components of the result of date.isoformat()
-    *
-    *  Return codes:
-    *       0:  Success
-    *      -1:  Failed to parse date component
-    *      -2:  Failed to parse dateseparator
-    */
+     *
+     *  Return codes:
+     *       0:  Success
+     *      -1:  Failed to parse date component
+     *      -2:  Failed to parse dateseparator
+     */
     const char *p = dtstr;
     p = parse_digits(p, year, 4);
     if (NULL == p) {
@@ -719,8 +720,9 @@ static int parse_isoformat_date(const char *dtstr,
 }
 
 static int
-parse_hh_mm_ss_ff(const char *tstr, const char *tstr_end,
-                  int* hour, int* minute, int *second, int *microsecond) {
+parse_hh_mm_ss_ff(const char *tstr, const char *tstr_end, int *hour,
+                  int *minute, int *second, int *microsecond)
+{
     const char *p = tstr;
     const char *p_end = tstr_end;
     int *vals[3] = {hour, minute, second};
@@ -735,12 +737,15 @@ parse_hh_mm_ss_ff(const char *tstr, const char *tstr_end,
         char c = *(p++);
         if (p >= p_end) {
             return c != '\0';
-        } else if (c == ':') {
+        }
+        else if (c == ':') {
             continue;
-        } else if (c == '.') {
+        }
+        else if (c == '.') {
             break;
-        } else {
-            return -4;      // Malformed time separator
+        }
+        else {
+            return -4;  // Malformed time separator
         }
     }
 
@@ -764,9 +769,10 @@ parse_hh_mm_ss_ff(const char *tstr, const char *tstr_end,
 }
 
 static int
-parse_isoformat_time(const char *dtstr, size_t dtlen,
-                     int* hour, int *minute, int *second, int *microsecond,
-                     int* tzoffset, int *tzmicrosecond) {
+parse_isoformat_time(const char *dtstr, size_t dtlen, int *hour, int *minute,
+                     int *second, int *microsecond, int *tzoffset,
+                     int *tzmicrosecond)
+{
     // Parse the time portion of a datetime.isoformat() string
     //
     // Return codes:
@@ -784,19 +790,21 @@ parse_isoformat_time(const char *dtstr, size_t dtlen,
         if (*tzinfo_pos == '+' || *tzinfo_pos == '-') {
             break;
         }
-    } while(++tzinfo_pos < p_end);
+    } while (++tzinfo_pos < p_end);
 
-    int rv = parse_hh_mm_ss_ff(dtstr, tzinfo_pos,
-                               hour, minute, second, microsecond);
+    int rv = parse_hh_mm_ss_ff(dtstr, tzinfo_pos, hour, minute, second,
+                               microsecond);
 
     if (rv < 0) {
         return rv;
-    } else if (tzinfo_pos == p_end) {
+    }
+    else if (tzinfo_pos == p_end) {
         // We know that there's no time zone, so if there's stuff at the
         // end of the string it's an error.
         if (rv == 1) {
             return -5;
-        } else {
+        }
+        else {
             return 0;
         }
     }
@@ -811,19 +819,18 @@ parse_isoformat_time(const char *dtstr, size_t dtlen,
         return -5;
     }
 
-    int tzsign = (*tzinfo_pos == '-')?-1:1;
+    int tzsign = (*tzinfo_pos == '-') ? -1 : 1;
     tzinfo_pos++;
     int tzhour = 0, tzminute = 0, tzsecond = 0;
-    rv = parse_hh_mm_ss_ff(tzinfo_pos, p_end,
-                           &tzhour, &tzminute, &tzsecond, tzmicrosecond);
+    rv = parse_hh_mm_ss_ff(tzinfo_pos, p_end, &tzhour, &tzminute, &tzsecond,
+                           tzmicrosecond);
 
     *tzoffset = tzsign * ((tzhour * 3600) + (tzminute * 60) + tzsecond);
     *tzmicrosecond *= tzsign;
 
-    return rv?-5:1;
+    return rv ? -5 : 1;
 }
 
-
 /* ---------------------------------------------------------------------------
  * Create various objects, mostly without range checking.
  */
@@ -838,30 +845,33 @@ new_date_ex(int year, int month, int day, PyTypeObject *type)
         return NULL;
     }
 
-    self = (PyDateTime_Date *) (type->tp_alloc(type, 0));
+    self = (PyDateTime_Date *)(type->tp_alloc(type, 0));
     if (self != NULL)
         set_date_fields(self, year, month, day);
-    return (PyObject *) self;
+    return (PyObject *)self;
 }
 
 #define new_date(year, month, day) \
     new_date_ex(year, month, day, &PyDateTime_DateType)
 
 // Forward declaration
-static PyObject * new_datetime_ex(int, int, int, int, int, int, int,
-                                  PyObject*, PyTypeObject*);
+static PyObject *
+new_datetime_ex(int, int, int, int, int, int, int, PyObject *, PyTypeObject *);
 
 /* Create date instance with no range checking, or call subclass constructor */
 static PyObject *
-new_date_subclass_ex(int year, int month, int day, PyObject *cls) {
+new_date_subclass_ex(int year, int month, int day, PyObject *cls)
+{
     PyObject *result;
     // We have "fast path" constructors for two subclasses: date and datetime
     if ((PyTypeObject *)cls == &PyDateTime_DateType) {
         result = new_date_ex(year, month, day, (PyTypeObject *)cls);
-    } else if ((PyTypeObject *)cls == &PyDateTime_DateTimeType) {
+    }
+    else if ((PyTypeObject *)cls == &PyDateTime_DateTimeType) {
         result = new_datetime_ex(year, month, day, 0, 0, 0, 0, Py_None,
                                  (PyTypeObject *)cls);
-    } else {
+    }
+    else {
         result = PyObject_CallFunction(cls, "iii", year, month, day);
     }
 
@@ -1280,7 +1290,8 @@ append_keyword_fold(PyObject *repr, int fold)
 }
 
 static inline PyObject *
-tzinfo_from_isoformat_results(int rv, int tzoffset, int tz_useconds) {
+tzinfo_from_isoformat_results(int rv, int tzoffset, int tz_useconds)
+{
     PyObject *tzinfo;
     if (rv == 1) {
         // Create a timezone from offset in seconds (0 returns UTC)
@@ -1295,7 +1306,8 @@ tzinfo_from_isoformat_results(int rv, int tzoffset, int tz_useconds) {
         }
         tzinfo = new_timezone(delta, NULL);
         Py_DECREF(delta);
-    } else {
+    }
+    else {
         tzinfo = Py_None;
         Py_INCREF(Py_None);
     }
@@ -1771,6 +1783,29 @@ Done:
     return result;
 }
 
+static PyObject *
+checked_divmod(PyObject *a, PyObject *b)
+{
+    PyObject *result = PyNumber_Divmod(a, b);
+    if (result != NULL) {
+        if (!PyTuple_Check(result)) {
+            PyErr_Format(PyExc_TypeError,
+                         "divmod() returned non-tuple (type %.200s)",
+                         result->ob_type->tp_name);
+            Py_DECREF(result);
+            return NULL;
+        }
+        if (PyTuple_GET_SIZE(result) != 2) {
+            PyErr_Format(PyExc_TypeError,
+                         "divmod() returned a tuple of size %zd",
+                         PyTuple_GET_SIZE(result));
+            Py_DECREF(result);
+            return NULL;
+        }
+    }
+    return result;
+}
+
 /* Convert a number of us (as a Python int) to a timedelta.
  */
 static PyObject *
@@ -1779,70 +1814,49 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type)
     int us;
     int s;
     int d;
-    long temp;
 
     PyObject *tuple = NULL;
     PyObject *num = NULL;
     PyObject *result = NULL;
 
-    assert(PyLong_CheckExact(pyus));
-    tuple = PyNumber_Divmod(pyus, us_per_second);
-    if (tuple == NULL)
+    tuple = checked_divmod(pyus, us_per_second);
+    if (tuple == NULL) {
         goto Done;
+    }
 
-    num = PyTuple_GetItem(tuple, 1);            /* us */
-    if (num == NULL)
-        goto Done;
-    temp = PyLong_AsLong(num);
+    num = PyTuple_GET_ITEM(tuple, 1);           /* us */
+    us = _PyLong_AsInt(num);
     num = NULL;
-    if (temp == -1 && PyErr_Occurred())
-        goto Done;
-    assert(0 <= temp && temp < 1000000);
-    us = (int)temp;
-    if (us < 0) {
-        /* The divisor was positive, so this must be an error. */
-        assert(PyErr_Occurred());
+    if (us == -1 && PyErr_Occurred()) {
         goto Done;
     }
+    if (!(0 <= us && us < 1000000)) {
+        goto BadDivmod;
+    }
 
-    num = PyTuple_GetItem(tuple, 0);            /* leftover seconds */
-    if (num == NULL)
-        goto Done;
+    num = PyTuple_GET_ITEM(tuple, 0);           /* leftover seconds */
     Py_INCREF(num);
     Py_DECREF(tuple);
 
-    tuple = PyNumber_Divmod(num, seconds_per_day);
+    tuple = checked_divmod(num, seconds_per_day);
     if (tuple == NULL)
         goto Done;
     Py_DECREF(num);
 
-    num = PyTuple_GetItem(tuple, 1);            /* seconds */
-    if (num == NULL)
-        goto Done;
-    temp = PyLong_AsLong(num);
+    num = PyTuple_GET_ITEM(tuple, 1);           /* seconds */
+    s = _PyLong_AsInt(num);
     num = NULL;
-    if (temp == -1 && PyErr_Occurred())
-        goto Done;
-    assert(0 <= temp && temp < 24*3600);
-    s = (int)temp;
-
-    if (s < 0) {
-        /* The divisor was positive, so this must be an error. */
-        assert(PyErr_Occurred());
+    if (s == -1 && PyErr_Occurred()) {
         goto Done;
     }
+    if (!(0 <= s && s < 24*3600)) {
+        goto BadDivmod;
+    }
 
-    num = PyTuple_GetItem(tuple, 0);            /* leftover days */
-    if (num == NULL)
-        goto Done;
+    num = PyTuple_GET_ITEM(tuple, 0);           /* leftover days */
     Py_INCREF(num);
-    temp = PyLong_AsLong(num);
-    if (temp == -1 && PyErr_Occurred())
-        goto Done;
-    d = (int)temp;
-    if ((long)d != temp) {
-        PyErr_SetString(PyExc_OverflowError, "normalized days too "
-                        "large to fit in a C int");
+    d = _PyLong_AsInt(num);
+    if (d == -1 && PyErr_Occurred()) {
         goto Done;
     }
     result = new_delta_ex(d, s, us, 0, type);
@@ -1851,6 +1865,11 @@ Done:
     Py_XDECREF(tuple);
     Py_XDECREF(num);
     return result;
+
+BadDivmod:
+    PyErr_SetString(PyExc_TypeError,
+                    "divmod() returned a value out of range");
+    goto Done;
 }
 
 #define microseconds_to_delta(pymicros) \
@@ -1867,7 +1886,7 @@ multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta)
     if (pyus_in == NULL)
         return NULL;
 
-    pyus_out = PyNumber_Multiply(pyus_in, intobj);
+    pyus_out = PyNumber_Multiply(intobj, pyus_in);
     Py_DECREF(pyus_in);
     if (pyus_out == NULL)
         return NULL;
@@ -2271,13 +2290,12 @@ delta_divmod(PyObject *left, PyObject *right)
         return NULL;
     }
 
-    divmod = PyNumber_Divmod(pyus_left, pyus_right);
+    divmod = checked_divmod(pyus_left, pyus_right);
     Py_DECREF(pyus_left);
     Py_DECREF(pyus_right);
     if (divmod == NULL)
         return NULL;
 
-    assert(PyTuple_Size(divmod) == 2);
     delta = microseconds_to_delta(PyTuple_GET_ITEM(divmod, 1));
     if (delta == NULL) {
         Py_DECREF(divmod);
@@ -2308,13 +2326,11 @@ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor,
     assert(num != NULL);
 
     if (PyLong_Check(num)) {
-        prod = PyNumber_Multiply(factor, num);
+        prod = PyNumber_Multiply(num, factor);
         if (prod == NULL)
             return NULL;
-        assert(PyLong_CheckExact(prod));
         sum = PyNumber_Add(sofar, prod);
         Py_DECREF(prod);
-        assert(sum == NULL || PyLong_CheckExact(sum));
         return sum;
     }
 
@@ -2372,7 +2388,6 @@ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor,
         Py_DECREF(sum);
         Py_DECREF(x);
         *leftover += fracpart;
-        assert(y == NULL || PyLong_CheckExact(y));
         return y;
     }
 
@@ -2640,7 +2655,11 @@ static PyMethodDef delta_methods[] = {
 };
 
 static const char delta_doc[] =
-PyDoc_STR("Difference between two datetime values.");
+PyDoc_STR("Difference between two datetime values.\n\n"
+          "timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, "
+          "minutes=0, hours=0, weeks=0)\n\n"
+          "All arguments are optional and default to 0.\n"
+          "Arguments may be integers or floats, and may be positive or negative.");
 
 static PyNumberMethods delta_as_number = {
     delta_add,                                  /* nb_add */
@@ -2756,29 +2775,60 @@ static PyGetSetDef date_getset[] = {
 static char *date_kws[] = {"year", "month", "day", NULL};
 
 static PyObject *
+date_from_pickle(PyTypeObject *type, PyObject *state)
+{
+    PyDateTime_Date *me;
+
+    me = (PyDateTime_Date *) (type->tp_alloc(type, 0));
+    if (me != NULL) {
+        const char *pdata = PyBytes_AS_STRING(state);
+        memcpy(me->data, pdata, _PyDateTime_DATE_DATASIZE);
+        me->hashcode = -1;
+    }
+    return (PyObject *)me;
+}
+
+static PyObject *
 date_new(PyTypeObject *type, PyObject *args, PyObject *kw)
 {
     PyObject *self = NULL;
-    PyObject *state;
     int year;
     int month;
     int day;
 
     /* Check for invocation from pickle with __getstate__ state */
-    if (PyTuple_GET_SIZE(args) == 1 &&
-        PyBytes_Check(state = PyTuple_GET_ITEM(args, 0)) &&
-        PyBytes_GET_SIZE(state) == _PyDateTime_DATE_DATASIZE &&
-        MONTH_IS_SANE(PyBytes_AS_STRING(state)[2]))
-    {
-        PyDateTime_Date *me;
-
-        me = (PyDateTime_Date *) (type->tp_alloc(type, 0));
-        if (me != NULL) {
-            char *pdata = PyBytes_AS_STRING(state);
-            memcpy(me->data, pdata, _PyDateTime_DATE_DATASIZE);
-            me->hashcode = -1;
+    if (PyTuple_GET_SIZE(args) == 1) {
+        PyObject *state = PyTuple_GET_ITEM(args, 0);
+        if (PyBytes_Check(state)) {
+            if (PyBytes_GET_SIZE(state) == _PyDateTime_DATE_DATASIZE &&
+                MONTH_IS_SANE(PyBytes_AS_STRING(state)[2]))
+            {
+                return date_from_pickle(type, state);
+            }
+        }
+        else if (PyUnicode_Check(state)) {
+            if (PyUnicode_READY(state)) {
+                return NULL;
+            }
+            if (PyUnicode_GET_LENGTH(state) == _PyDateTime_DATE_DATASIZE &&
+                MONTH_IS_SANE(PyUnicode_READ_CHAR(state, 2)))
+            {
+                state = PyUnicode_AsLatin1String(state);
+                if (state == NULL) {
+                    if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError)) {
+                        /* More informative error message. */
+                        PyErr_SetString(PyExc_ValueError,
+                            "Failed to encode latin1 string when unpickling "
+                            "a date object. "
+                            "pickle.load(data, encoding='latin1') is assumed.");
+                    }
+                    return NULL;
+                }
+                self = date_from_pickle(type, state);
+                Py_DECREF(state);
+                return self;
+            }
         }
-        return (PyObject *)me;
     }
 
     if (PyArg_ParseTupleAndKeywords(args, kw, "iii", date_kws,
@@ -2875,17 +2925,19 @@ date_fromordinal(PyObject *cls, PyObject *args)
 
 /* Return the new date from a string as generated by date.isoformat() */
 static PyObject *
-date_fromisoformat(PyObject *cls, PyObject *dtstr) {
+date_fromisoformat(PyObject *cls, PyObject *dtstr)
+{
     assert(dtstr != NULL);
 
     if (!PyUnicode_Check(dtstr)) {
-        PyErr_SetString(PyExc_TypeError, "fromisoformat: argument must be str");
+        PyErr_SetString(PyExc_TypeError,
+                        "fromisoformat: argument must be str");
         return NULL;
     }
 
     Py_ssize_t len;
 
-    const char * dt_ptr = PyUnicode_AsUTF8AndSize(dtstr, &len);
+    const char *dt_ptr = PyUnicode_AsUTF8AndSize(dtstr, &len);
     if (dt_ptr == NULL) {
         goto invalid_string_error;
     }
@@ -2895,7 +2947,8 @@ date_fromisoformat(PyObject *cls, PyObject *dtstr) {
     int rv;
     if (len == 10) {
         rv = parse_isoformat_date(dt_ptr, &year, &month, &day);
-    } else {
+    }
+    else {
         rv = -1;
     }
 
@@ -2906,12 +2959,10 @@ date_fromisoformat(PyObject *cls, PyObject *dtstr) {
     return new_date_subclass_ex(year, month, day, cls);
 
 invalid_string_error:
-    PyErr_Format(PyExc_ValueError, "Invalid isoformat string: %R",
-                 dtstr);
+    PyErr_Format(PyExc_ValueError, "Invalid isoformat string: %R", dtstr);
     return NULL;
 }
 
-
 /*
  * Date arithmetic.
  */
@@ -3426,7 +3477,7 @@ tzinfo_fromutc(PyDateTime_TZInfo *self, PyObject *dt)
     return result;
 
 Inconsistent:
-    PyErr_SetString(PyExc_ValueError, "fromutc: tz.dst() gave"
+    PyErr_SetString(PyExc_ValueError, "fromutc: tz.dst() gave "
                     "inconsistent results; cannot convert");
 
     /* fall through to failure */
@@ -3864,10 +3915,42 @@ static char *time_kws[] = {"hour", "minute", "second", "microsecond",
                            "tzinfo", "fold", NULL};
 
 static PyObject *
+time_from_pickle(PyTypeObject *type, PyObject *state, PyObject *tzinfo)
+{
+    PyDateTime_Time *me;
+    char aware = (char)(tzinfo != Py_None);
+
+    if (aware && check_tzinfo_subclass(tzinfo) < 0) {
+        PyErr_SetString(PyExc_TypeError, "bad tzinfo state arg");
+        return NULL;
+    }
+
+    me = (PyDateTime_Time *) (type->tp_alloc(type, aware));
+    if (me != NULL) {
+        const char *pdata = PyBytes_AS_STRING(state);
+
+        memcpy(me->data, pdata, _PyDateTime_TIME_DATASIZE);
+        me->hashcode = -1;
+        me->hastzinfo = aware;
+        if (aware) {
+            Py_INCREF(tzinfo);
+            me->tzinfo = tzinfo;
+        }
+        if (pdata[0] & (1 << 7)) {
+            me->data[0] -= 128;
+            me->fold = 1;
+        }
+        else {
+            me->fold = 0;
+        }
+    }
+    return (PyObject *)me;
+}
+
+static PyObject *
 time_new(PyTypeObject *type, PyObject *args, PyObject *kw)
 {
     PyObject *self = NULL;
-    PyObject *state;
     int hour = 0;
     int minute = 0;
     int second = 0;
@@ -3876,44 +3959,42 @@ time_new(PyTypeObject *type, PyObject *args, PyObject *kw)
     int fold = 0;
 
     /* Check for invocation from pickle with __getstate__ state */
-    if (PyTuple_GET_SIZE(args) >= 1 &&
-        PyTuple_GET_SIZE(args) <= 2 &&
-        PyBytes_Check(state = PyTuple_GET_ITEM(args, 0)) &&
-        PyBytes_GET_SIZE(state) == _PyDateTime_TIME_DATASIZE &&
-        (0x7F & ((unsigned char) (PyBytes_AS_STRING(state)[0]))) < 24)
-    {
-        PyDateTime_Time *me;
-        char aware;
-
+    if (PyTuple_GET_SIZE(args) >= 1 && PyTuple_GET_SIZE(args) <= 2) {
+        PyObject *state = PyTuple_GET_ITEM(args, 0);
         if (PyTuple_GET_SIZE(args) == 2) {
             tzinfo = PyTuple_GET_ITEM(args, 1);
-            if (check_tzinfo_subclass(tzinfo) < 0) {
-                PyErr_SetString(PyExc_TypeError, "bad "
-                    "tzinfo state arg");
-                return NULL;
-            }
         }
-        aware = (char)(tzinfo != Py_None);
-        me = (PyDateTime_Time *) (type->tp_alloc(type, aware));
-        if (me != NULL) {
-            char *pdata = PyBytes_AS_STRING(state);
-
-            memcpy(me->data, pdata, _PyDateTime_TIME_DATASIZE);
-            me->hashcode = -1;
-            me->hastzinfo = aware;
-            if (aware) {
-                Py_INCREF(tzinfo);
-                me->tzinfo = tzinfo;
+        if (PyBytes_Check(state)) {
+            if (PyBytes_GET_SIZE(state) == _PyDateTime_TIME_DATASIZE &&
+                (0x7F & ((unsigned char) (PyBytes_AS_STRING(state)[0]))) < 24)
+            {
+                return time_from_pickle(type, state, tzinfo);
             }
-            if (pdata[0] & (1 << 7)) {
-                me->data[0] -= 128;
-                me->fold = 1;
+        }
+        else if (PyUnicode_Check(state)) {
+            if (PyUnicode_READY(state)) {
+                return NULL;
             }
-            else {
-                me->fold = 0;
+            if (PyUnicode_GET_LENGTH(state) == _PyDateTime_TIME_DATASIZE &&
+                (0x7F & PyUnicode_READ_CHAR(state, 2)) < 24)
+            {
+                state = PyUnicode_AsLatin1String(state);
+                if (state == NULL) {
+                    if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError)) {
+                        /* More informative error message. */
+                        PyErr_SetString(PyExc_ValueError,
+                            "Failed to encode latin1 string when unpickling "
+                            "a time object. "
+                            "pickle.load(data, encoding='latin1') is assumed.");
+                    }
+                    return NULL;
+                }
+                self = time_from_pickle(type, state, tzinfo);
+                Py_DECREF(state);
+                return self;
             }
         }
-        return (PyObject *)me;
+        tzinfo = Py_None;
     }
 
     if (PyArg_ParseTupleAndKeywords(args, kw, "|iiiiO$i", time_kws,
@@ -4500,10 +4581,42 @@ static char *datetime_kws[] = {
 };
 
 static PyObject *
+datetime_from_pickle(PyTypeObject *type, PyObject *state, PyObject *tzinfo)
+{
+    PyDateTime_DateTime *me;
+    char aware = (char)(tzinfo != Py_None);
+
+    if (aware && check_tzinfo_subclass(tzinfo) < 0) {
+        PyErr_SetString(PyExc_TypeError, "bad tzinfo state arg");
+        return NULL;
+    }
+
+    me = (PyDateTime_DateTime *) (type->tp_alloc(type , aware));
+    if (me != NULL) {
+        const char *pdata = PyBytes_AS_STRING(state);
+
+        memcpy(me->data, pdata, _PyDateTime_DATETIME_DATASIZE);
+        me->hashcode = -1;
+        me->hastzinfo = aware;
+        if (aware) {
+            Py_INCREF(tzinfo);
+            me->tzinfo = tzinfo;
+        }
+        if (pdata[2] & (1 << 7)) {
+            me->data[2] -= 128;
+            me->fold = 1;
+        }
+        else {
+            me->fold = 0;
+        }
+    }
+    return (PyObject *)me;
+}
+
+static PyObject *
 datetime_new(PyTypeObject *type, PyObject *args, PyObject *kw)
 {
     PyObject *self = NULL;
-    PyObject *state;
     int year;
     int month;
     int day;
@@ -4515,44 +4628,42 @@ datetime_new(PyTypeObject *type, PyObject *args, PyObject *kw)
     PyObject *tzinfo = Py_None;
 
     /* Check for invocation from pickle with __getstate__ state */
-    if (PyTuple_GET_SIZE(args) >= 1 &&
-        PyTuple_GET_SIZE(args) <= 2 &&
-        PyBytes_Check(state = PyTuple_GET_ITEM(args, 0)) &&
-        PyBytes_GET_SIZE(state) == _PyDateTime_DATETIME_DATASIZE &&
-        MONTH_IS_SANE(PyBytes_AS_STRING(state)[2] & 0x7F))
-    {
-        PyDateTime_DateTime *me;
-        char aware;
-
+    if (PyTuple_GET_SIZE(args) >= 1 && PyTuple_GET_SIZE(args) <= 2) {
+        PyObject *state = PyTuple_GET_ITEM(args, 0);
         if (PyTuple_GET_SIZE(args) == 2) {
             tzinfo = PyTuple_GET_ITEM(args, 1);
-            if (check_tzinfo_subclass(tzinfo) < 0) {
-                PyErr_SetString(PyExc_TypeError, "bad "
-                    "tzinfo state arg");
-                return NULL;
-            }
         }
-        aware = (char)(tzinfo != Py_None);
-        me = (PyDateTime_DateTime *) (type->tp_alloc(type , aware));
-        if (me != NULL) {
-            char *pdata = PyBytes_AS_STRING(state);
-
-            memcpy(me->data, pdata, _PyDateTime_DATETIME_DATASIZE);
-            me->hashcode = -1;
-            me->hastzinfo = aware;
-            if (aware) {
-                Py_INCREF(tzinfo);
-                me->tzinfo = tzinfo;
+        if (PyBytes_Check(state)) {
+            if (PyBytes_GET_SIZE(state) == _PyDateTime_DATETIME_DATASIZE &&
+                MONTH_IS_SANE(PyBytes_AS_STRING(state)[2] & 0x7F))
+            {
+                return datetime_from_pickle(type, state, tzinfo);
             }
-            if (pdata[2] & (1 << 7)) {
-                me->data[2] -= 128;
-                me->fold = 1;
+        }
+        else if (PyUnicode_Check(state)) {
+            if (PyUnicode_READY(state)) {
+                return NULL;
             }
-            else {
-                me->fold = 0;
+            if (PyUnicode_GET_LENGTH(state) == _PyDateTime_DATETIME_DATASIZE &&
+                MONTH_IS_SANE(PyUnicode_READ_CHAR(state, 2) & 0x7F))
+            {
+                state = PyUnicode_AsLatin1String(state);
+                if (state == NULL) {
+                    if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError)) {
+                        /* More informative error message. */
+                        PyErr_SetString(PyExc_ValueError,
+                            "Failed to encode latin1 string when unpickling "
+                            "a datetime object. "
+                            "pickle.load(data, encoding='latin1') is assumed.");
+                    }
+                    return NULL;
+                }
+                self = datetime_from_pickle(type, state, tzinfo);
+                Py_DECREF(state);
+                return self;
             }
         }
-        return (PyObject *)me;
+        tzinfo = Py_None;
     }
 
     if (PyArg_ParseTupleAndKeywords(args, kw, "iii|iiiiO$i", datetime_kws,
@@ -4856,52 +4967,65 @@ datetime_combine(PyObject *cls, PyObject *args, PyObject *kw)
 }
 
 static PyObject *
-_sanitize_isoformat_str(PyObject *dtstr, int *needs_decref) {
+_sanitize_isoformat_str(PyObject *dtstr)
+{
     // `fromisoformat` allows surrogate characters in exactly one position,
     // the separator; to allow datetime_fromisoformat to make the simplifying
     // assumption that all valid strings can be encoded in UTF-8, this function
     // replaces any surrogate character separators with `T`.
+    //
+    // The result of this, if not NULL, returns a new reference
     Py_ssize_t len = PyUnicode_GetLength(dtstr);
-    *needs_decref = 0;
-    if (len <= 10 || !Py_UNICODE_IS_SURROGATE(PyUnicode_READ_CHAR(dtstr, 10))) {
+    if (len < 0) {
+        return NULL;
+    }
+
+    if (len <= 10 ||
+        !Py_UNICODE_IS_SURROGATE(PyUnicode_READ_CHAR(dtstr, 10))) {
+        Py_INCREF(dtstr);
         return dtstr;
     }
 
-    PyObject *str_out = PyUnicode_New(len, PyUnicode_MAX_CHAR_VALUE(dtstr));
+    PyObject *str_out = _PyUnicode_Copy(dtstr);
     if (str_out == NULL) {
         return NULL;
     }
 
-    if (PyUnicode_CopyCharacters(str_out, 0, dtstr, 0, len) == -1 ||
-            PyUnicode_WriteChar(str_out, 10, (Py_UCS4)'T')) {
+    if (PyUnicode_WriteChar(str_out, 10, (Py_UCS4)'T')) {
         Py_DECREF(str_out);
         return NULL;
     }
 
-    *needs_decref = 1;
     return str_out;
 }
 
 static PyObject *
-datetime_fromisoformat(PyObject* cls, PyObject *dtstr) {
+datetime_fromisoformat(PyObject *cls, PyObject *dtstr)
+{
     assert(dtstr != NULL);
 
     if (!PyUnicode_Check(dtstr)) {
-        PyErr_SetString(PyExc_TypeError, "fromisoformat: argument must be str");
+        PyErr_SetString(PyExc_TypeError,
+                        "fromisoformat: argument must be str");
         return NULL;
     }
 
-    int needs_decref = 0;
-    dtstr = _sanitize_isoformat_str(dtstr, &needs_decref);
-    if (dtstr == NULL) {
+    PyObject *dtstr_clean = _sanitize_isoformat_str(dtstr);
+    if (dtstr_clean == NULL) {
         goto error;
     }
 
     Py_ssize_t len;
-    const char * dt_ptr = PyUnicode_AsUTF8AndSize(dtstr, &len);
+    const char *dt_ptr = PyUnicode_AsUTF8AndSize(dtstr_clean, &len);
 
     if (dt_ptr == NULL) {
-        goto invalid_string_error;
+        if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError)) {
+            // Encoding errors are invalid string errors at this point
+            goto invalid_string_error;
+        }
+        else {
+            goto error;
+        }
     }
 
     const char *p = dt_ptr;
@@ -4917,8 +5041,9 @@ datetime_fromisoformat(PyObject* cls, PyObject *dtstr) {
         // In UTF-8, the length of multi-byte characters is encoded in the MSB
         if ((p[10] & 0x80) == 0) {
             p += 11;
-        } else {
-            switch(p[10] & 0xf0) {
+        }
+        else {
+            switch (p[10] & 0xf0) {
                 case 0xe0:
                     p += 13;
                     break;
@@ -4932,15 +5057,14 @@ datetime_fromisoformat(PyObject* cls, PyObject *dtstr) {
         }
 
         len -= (p - dt_ptr);
-        rv = parse_isoformat_time(p, len,
-                                  &hour, &minute, &second, &microsecond,
-                                  &tzoffset, &tzusec);
+        rv = parse_isoformat_time(p, len, &hour, &minute, &second,
+                                  &microsecond, &tzoffset, &tzusec);
     }
     if (rv < 0) {
         goto invalid_string_error;
     }
 
-    PyObjecttzinfo = tzinfo_from_isoformat_results(rv, tzoffset, tzusec);
+    PyObject *tzinfo = tzinfo_from_isoformat_results(rv, tzoffset, tzusec);
     if (tzinfo == NULL) {
         goto error;
     }
@@ -4949,23 +5073,18 @@ datetime_fromisoformat(PyObject* cls, PyObject *dtstr) {
                                             second, microsecond, tzinfo, cls);
 
     Py_DECREF(tzinfo);
-    if (needs_decref) {
-        Py_DECREF(dtstr);
-    }
+    Py_DECREF(dtstr_clean);
     return dt;
 
 invalid_string_error:
     PyErr_Format(PyExc_ValueError, "Invalid isoformat string: %R", dtstr);
 
 error:
-    if (needs_decref) {
-        Py_DECREF(dtstr);
-    }
+    Py_XDECREF(dtstr_clean);
 
     return NULL;
 }
 
-
 /*
  * Destructor.
  */
index daf2b9d..a67dd9b 100644 (file)
@@ -818,12 +818,12 @@ void *mpd_sh_alloc(mpd_size_t struct_size, mpd_size_t nmemb, mpd_size_t size);
 mpd_t *mpd_qnew(void);
 mpd_t *mpd_new(mpd_context_t *ctx);
 mpd_t *mpd_qnew_size(mpd_ssize_t size);
-void mpd_del(mpd_t *dec);
+EXTINLINE void mpd_del(mpd_t *dec);
 
-void mpd_uint_zero(mpd_uint_t *dest, mpd_size_t len);
-int mpd_qresize(mpd_t *result, mpd_ssize_t size, uint32_t *status);
-int mpd_qresize_zero(mpd_t *result, mpd_ssize_t size, uint32_t *status);
-void mpd_minalloc(mpd_t *result);
+EXTINLINE void mpd_uint_zero(mpd_uint_t *dest, mpd_size_t len);
+EXTINLINE int mpd_qresize(mpd_t *result, mpd_ssize_t size, uint32_t *status);
+EXTINLINE int mpd_qresize_zero(mpd_t *result, mpd_ssize_t size, uint32_t *status);
+EXTINLINE void mpd_minalloc(mpd_t *result);
 
 int mpd_resize(mpd_t *result, mpd_ssize_t size, mpd_context_t *ctx);
 int mpd_resize_zero(mpd_t *result, mpd_ssize_t size, mpd_context_t *ctx);
index 4b86f96..ff3a240 100644 (file)
@@ -204,6 +204,8 @@ typedef struct {
 
 
 #define Element_CheckExact(op) (Py_TYPE(op) == &Element_Type)
+#define Element_Check(op) PyObject_TypeCheck(op, &Element_Type)
+
 
 /* -------------------------------------------------------------------- */
 /* Element constructors and destructor */
@@ -231,11 +233,29 @@ create_extra(ElementObject* self, PyObject* attrib)
 }
 
 LOCAL(void)
-dealloc_extra(ElementObject* self)
+dealloc_extra(ElementObjectExtra *extra)
 {
-    ElementObjectExtra *myextra;
     Py_ssize_t i;
 
+    if (!extra)
+        return;
+
+    Py_DECREF(extra->attrib);
+
+    for (i = 0; i < extra->length; i++)
+        Py_DECREF(extra->children[i]);
+
+    if (extra->children != extra->_children)
+        PyObject_Free(extra->children);
+
+    PyObject_Free(extra);
+}
+
+LOCAL(void)
+clear_extra(ElementObject* self)
+{
+    ElementObjectExtra *myextra;
+
     if (!self->extra)
         return;
 
@@ -244,15 +264,7 @@ dealloc_extra(ElementObject* self)
     myextra = self->extra;
     self->extra = NULL;
 
-    Py_DECREF(myextra->attrib);
-
-    for (i = 0; i < myextra->length; i++)
-        Py_DECREF(myextra->children[i]);
-
-    if (myextra->children != myextra->_children)
-        PyObject_Free(myextra->children);
-
-    PyObject_Free(myextra);
+    dealloc_extra(myextra);
 }
 
 /* Convenience internal function to create new Element objects with the given
@@ -324,6 +336,9 @@ static PyObject*
 get_attrib_from_keywords(PyObject *kwds)
 {
     PyObject *attrib_str = PyUnicode_FromString("attrib");
+    if (attrib_str == NULL) {
+        return NULL;
+    }
     PyObject *attrib = PyDict_GetItem(kwds, attrib_str);
 
     if (attrib) {
@@ -337,17 +352,20 @@ get_attrib_from_keywords(PyObject *kwds)
             return NULL;
         }
         attrib = PyDict_Copy(attrib);
-        PyDict_DelItem(kwds, attrib_str);
+        if (attrib && PyDict_DelItem(kwds, attrib_str) < 0) {
+            Py_DECREF(attrib);
+            attrib = NULL;
+        }
     } else {
         attrib = PyDict_New();
     }
 
     Py_DECREF(attrib_str);
 
-    /* attrib can be NULL if PyDict_New failed */
-    if (attrib)
-        if (PyDict_Update(attrib, kwds) < 0)
-            return NULL;
+    if (attrib != NULL && PyDict_Update(attrib, kwds) < 0) {
+        Py_DECREF(attrib);
+        return NULL;
+    }
     return attrib;
 }
 
@@ -418,6 +436,7 @@ element_resize(ElementObject* self, Py_ssize_t extra)
     Py_ssize_t size;
     PyObject* *children;
 
+    assert(extra >= 0);
     /* make sure self->children can hold the given number of extra
        elements.  set an exception and return -1 if allocation failed */
 
@@ -566,10 +585,9 @@ subelement(PyObject *self, PyObject *args, PyObject *kwds)
         attrib = PyDict_Copy(attrib);
         if (!attrib)
             return NULL;
-        if (kwds) {
-            if (PyDict_Update(attrib, kwds) < 0) {
-                return NULL;
-            }
+        if (kwds != NULL && PyDict_Update(attrib, kwds) < 0) {
+            Py_DECREF(attrib);
+            return NULL;
         }
     } else if (kwds) {
         /* have keyword args */
@@ -622,7 +640,7 @@ element_gc_clear(ElementObject *self)
     /* After dropping all references from extra, it's no longer valid anyway,
      * so fully deallocate it.
     */
-    dealloc_extra(self);
+    clear_extra(self);
     return 0;
 }
 
@@ -674,7 +692,7 @@ static PyObject *
 _elementtree_Element_clear_impl(ElementObject *self)
 /*[clinic end generated code: output=8bcd7a51f94cfff6 input=3c719ff94bf45dd6]*/
 {
-    dealloc_extra(self);
+    clear_extra(self);
 
     Py_INCREF(Py_None);
     _set_joined_ptr(&self->text, Py_None);
@@ -708,6 +726,7 @@ _elementtree_Element___copy___impl(ElementObject *self)
     Py_INCREF(JOIN_OBJ(self->tail));
     _set_joined_ptr(&element->tail, self->tail);
 
+    assert(!element->extra || !element->extra->length);
     if (self->extra) {
         if (element_resize(element, self->extra->length) < 0) {
             Py_DECREF(element);
@@ -719,6 +738,7 @@ _elementtree_Element___copy___impl(ElementObject *self)
             element->extra->children[i] = self->extra->children[i];
         }
 
+        assert(!element->extra->length);
         element->extra->length = self->extra->length;
     }
 
@@ -781,6 +801,7 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo)
         goto error;
     _set_joined_ptr(&element->tail, JOIN_SET(tail, JOIN_GET(self->tail)));
 
+    assert(!element->extra || !element->extra->length);
     if (self->extra) {
         if (element_resize(element, self->extra->length) < 0)
             goto error;
@@ -794,6 +815,7 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo)
             element->extra->children[i] = child;
         }
 
+        assert(!element->extra->length);
         element->extra->length = self->extra->length;
     }
 
@@ -955,6 +977,7 @@ element_setstate_from_attributes(ElementObject *self,
                                  PyObject *children)
 {
     Py_ssize_t i, nchildren;
+    ElementObjectExtra *oldextra = NULL;
 
     if (!tag) {
         PyErr_SetString(PyExc_TypeError, "tag may not be NULL");
@@ -973,8 +996,9 @@ element_setstate_from_attributes(ElementObject *self,
     _set_joined_ptr(&self->tail, tail);
 
     /* Handle ATTRIB and CHILDREN. */
-    if (!children && !attrib)
+    if (!children && !attrib) {
         Py_RETURN_NONE;
+    }
 
     /* Compute 'nchildren'. */
     if (children) {
@@ -982,32 +1006,48 @@ element_setstate_from_attributes(ElementObject *self,
             PyErr_SetString(PyExc_TypeError, "'_children' is not a list");
             return NULL;
         }
-        nchildren = PyList_Size(children);
-    }
-    else {
-        nchildren = 0;
-    }
+        nchildren = PyList_GET_SIZE(children);
 
-    /* Allocate 'extra'. */
-    if (element_resize(self, nchildren)) {
-        return NULL;
-    }
-    assert(self->extra && self->extra->allocated >= nchildren);
+        /* (Re-)allocate 'extra'.
+           Avoid DECREFs calling into this code again (cycles, etc.)
+         */
+        oldextra = self->extra;
+        self->extra = NULL;
+        if (element_resize(self, nchildren)) {
+            assert(!self->extra || !self->extra->length);
+            clear_extra(self);
+            self->extra = oldextra;
+            return NULL;
+        }
+        assert(self->extra);
+        assert(self->extra->allocated >= nchildren);
+        if (oldextra) {
+            assert(self->extra->attrib == Py_None);
+            self->extra->attrib = oldextra->attrib;
+            oldextra->attrib = Py_None;
+        }
 
-    /* Copy children */
-    for (i = 0; i < nchildren; i++) {
-        self->extra->children[i] = PyList_GET_ITEM(children, i);
-        Py_INCREF(self->extra->children[i]);
-    }
+        /* Copy children */
+        for (i = 0; i < nchildren; i++) {
+            self->extra->children[i] = PyList_GET_ITEM(children, i);
+            Py_INCREF(self->extra->children[i]);
+        }
 
-    self->extra->length = nchildren;
-    self->extra->allocated = nchildren;
+        assert(!self->extra->length);
+        self->extra->length = nchildren;
+    }
+    else {
+        if (element_resize(self, 0)) {
+            return NULL;
+        }
+    }
 
     /* Stash attrib. */
     if (attrib) {
         Py_INCREF(attrib);
         Py_XSETREF(self->extra->attrib, attrib);
     }
+    dealloc_extra(oldextra);
 
     Py_RETURN_NONE;
 }
@@ -1132,7 +1172,7 @@ _elementtree_Element_extend(ElementObject *self, PyObject *elements)
     for (i = 0; i < PySequence_Fast_GET_SIZE(seq); i++) {
         PyObject* element = PySequence_Fast_GET_ITEM(seq, i);
         Py_INCREF(element);
-        if (!PyObject_TypeCheck(element, (PyTypeObject *)&Element_Type)) {
+        if (!Element_Check(element)) {
             PyErr_Format(
                 PyExc_TypeError,
                 "expected an Element, not \"%.200s\"",
@@ -1184,7 +1224,7 @@ _elementtree_Element_find_impl(ElementObject *self, PyObject *path,
     for (i = 0; i < self->extra->length; i++) {
         PyObject* item = self->extra->children[i];
         int rc;
-        if (!Element_CheckExact(item))
+        if (!Element_Check(item))
             continue;
         Py_INCREF(item);
         rc = PyObject_RichCompareBool(((ElementObject*)item)->tag, path, Py_EQ);
@@ -1229,14 +1269,14 @@ _elementtree_Element_findtext_impl(ElementObject *self, PyObject *path,
     }
 
     for (i = 0; i < self->extra->length; i++) {
-        ElementObject* item = (ElementObject*) self->extra->children[i];
+        PyObject *item = self->extra->children[i];
         int rc;
-        if (!Element_CheckExact(item))
+        if (!Element_Check(item))
             continue;
         Py_INCREF(item);
-        rc = PyObject_RichCompareBool(item->tag, path, Py_EQ);
+        rc = PyObject_RichCompareBool(((ElementObject*)item)->tag, path, Py_EQ);
         if (rc > 0) {
-            PyObject* text = element_get_text(item);
+            PyObject* text = element_get_text((ElementObject*)item);
             if (text == Py_None) {
                 Py_DECREF(item);
                 return PyUnicode_New(0, 0);
@@ -1269,13 +1309,12 @@ _elementtree_Element_findall_impl(ElementObject *self, PyObject *path,
 {
     Py_ssize_t i;
     PyObject* out;
-    PyObject* tag = path;
     elementtreestate *st = ET_STATE_GLOBAL;
 
-    if (checkpath(tag) || namespaces != Py_None) {
+    if (checkpath(path) || namespaces != Py_None) {
         _Py_IDENTIFIER(findall);
         return _PyObject_CallMethodIdObjArgs(
-            st->elementpath_obj, &PyId_findall, self, tag, namespaces, NULL
+            st->elementpath_obj, &PyId_findall, self, path, namespaces, NULL
             );
     }
 
@@ -1289,10 +1328,10 @@ _elementtree_Element_findall_impl(ElementObject *self, PyObject *path,
     for (i = 0; i < self->extra->length; i++) {
         PyObject* item = self->extra->children[i];
         int rc;
-        if (!Element_CheckExact(item))
+        if (!Element_Check(item))
             continue;
         Py_INCREF(item);
-        rc = PyObject_RichCompareBool(((ElementObject*)item)->tag, tag, Py_EQ);
+        rc = PyObject_RichCompareBool(((ElementObject*)item)->tag, path, Py_EQ);
         if (rc != 0 && (rc < 0 || PyList_Append(out, item) < 0)) {
             Py_DECREF(item);
             Py_DECREF(out);
@@ -1823,7 +1862,6 @@ element_ass_subscr(PyObject* self_, PyObject* item, PyObject* value)
              * scheduled for removal.
             */
             if (!(recycle = PyList_New(slicelen))) {
-                PyErr_NoMemory();
                 return -1;
             }
 
@@ -1863,7 +1901,7 @@ element_ass_subscr(PyObject* self_, PyObject* item, PyObject* value)
             self->extra->length -= slicelen;
 
             /* Discard the recycle list with all the deleted sub-elements */
-            Py_XDECREF(recycle);
+            Py_DECREF(recycle);
             return 0;
         }
 
@@ -2174,7 +2212,7 @@ elementiter_next(ElementIterObject *it)
                 continue;
             }
 
-            if (!PyObject_TypeCheck(extra->children[child_index], &Element_Type)) {
+            if (!Element_Check(extra->children[child_index])) {
                 PyErr_Format(PyExc_AttributeError,
                              "'%.100s' object has no attribute 'iter'",
                              Py_TYPE(extra->children[child_index])->tp_name);
@@ -3318,7 +3356,6 @@ _elementtree_XMLParser___init___impl(XMLParserObject *self, PyObject *html,
         if (!target) {
             Py_CLEAR(self->entity);
             Py_CLEAR(self->names);
-            EXPAT(ParserFree)(self->parser);
             return -1;
         }
     }
index 40cd632..31d0540 100644 (file)
@@ -187,7 +187,7 @@ EVP_copy(EVPobject *self, PyObject *unused)
 }
 
 PyDoc_STRVAR(EVP_digest__doc__,
-"Return the digest value as a string of binary data.");
+"Return the digest value as a bytes object.");
 
 static PyObject *
 EVP_digest(EVPobject *self, PyObject *unused)
index ba33f8c..e4d637c 100644 (file)
@@ -201,7 +201,7 @@ write_bytes(bytesio *self, const char *bytes, Py_ssize_t len)
 }
 
 static PyObject *
-bytesio_get_closed(bytesio *self)
+bytesio_get_closed(bytesio *self, void *Py_UNUSED(ignored))
 {
     if (self->buf == NULL) {
         Py_RETURN_TRUE;
index 0d5bf3b..8bbe1ce 100644 (file)
@@ -791,11 +791,9 @@ _io_FileIO_read_impl(fileio *self, Py_ssize_t size)
     if (size < 0)
         return _io_FileIO_readall_impl(self);
 
-#ifdef MS_WINDOWS
-    /* On Windows, the count parameter of read() is an int */
-    if (size > INT_MAX)
-        size = INT_MAX;
-#endif
+    if (size > _PY_READ_MAX) {
+        size = _PY_READ_MAX;
+    }
 
     bytes = PyBytes_FromStringAndSize(NULL, size);
     if (bytes == NULL)
index d6b1e94..9d0d9ca 100644 (file)
@@ -261,7 +261,7 @@ _io_IncrementalNewlineDecoder___init___impl(nldecoder_object *self,
     }
     Py_INCREF(self->errors);
 
-    self->translate = translate;
+    self->translate = translate ? 1 : 0;
     self->seennl = 0;
     self->pendingcr = 0;
 
@@ -1313,7 +1313,7 @@ _io_TextIOWrapper_reconfigure_impl(textio *self, PyObject *encoding,
     /* Check if something is in the read buffer */
     if (self->decoded_chars != NULL) {
         if (encoding != Py_None || errors != Py_None || newline_obj != NULL) {
-            _unsupported("It is not possible to set the encoding or newline"
+            _unsupported("It is not possible to set the encoding or newline "
                          "of stream after the first read");
             return NULL;
         }
index 4d3d695..c11c1e0 100644 (file)
@@ -815,11 +815,13 @@ _io__WindowsConsoleIO_readall_impl(winconsoleio *self)
             }
             bufsize = newsize;
 
-            buf = PyMem_Realloc(buf, (bufsize + 1) * sizeof(wchar_t));
-            if (!buf) {
+            wchar_t *tmp = PyMem_Realloc(buf,
+                                         (bufsize + 1) * sizeof(wchar_t));
+            if (tmp == NULL) {
                 PyMem_Free(buf);
                 return NULL;
             }
+            buf = tmp;
         }
 
         subbuf = read_console_w(self->handle, bufsize - len, &n);
index f9eeeb7..8efda35 100644 (file)
@@ -70,20 +70,13 @@ copy_grouping(const char* s)
     do {
         i++;
         val = PyLong_FromLong(s[i]);
-        if (!val)
-            break;
-        if (PyList_SetItem(result, i, val)) {
-            Py_DECREF(val);
-            val = NULL;
-            break;
+        if (val == NULL) {
+            Py_DECREF(result);
+            return NULL;
         }
+        PyList_SET_ITEM(result, i, val);
     } while (s[i] != '\0' && s[i] != CHAR_MAX);
 
-    if (!val) {
-        Py_DECREF(result);
-        return NULL;
-    }
-
     return result;
 }
 
@@ -128,6 +121,82 @@ PyLocale_setlocale(PyObject* self, PyObject* args)
     return result_object;
 }
 
+static int
+locale_is_ascii(const char *str)
+{
+    return (strlen(str) == 1 && ((unsigned char)str[0]) <= 127);
+}
+
+static int
+locale_decode_monetary(PyObject *dict, struct lconv *lc)
+{
+    int change_locale;
+    change_locale = (!locale_is_ascii(lc->int_curr_symbol)
+                     || !locale_is_ascii(lc->currency_symbol)
+                     || !locale_is_ascii(lc->mon_decimal_point)
+                     || !locale_is_ascii(lc->mon_thousands_sep));
+
+    /* Keep a copy of the LC_CTYPE locale */
+    char *oldloc = NULL, *loc = NULL;
+    if (change_locale) {
+        oldloc = setlocale(LC_CTYPE, NULL);
+        if (!oldloc) {
+            PyErr_SetString(PyExc_RuntimeWarning,
+                            "failed to get LC_CTYPE locale");
+            return -1;
+        }
+
+        oldloc = _PyMem_Strdup(oldloc);
+        if (!oldloc) {
+            PyErr_NoMemory();
+            return -1;
+        }
+
+        loc = setlocale(LC_MONETARY, NULL);
+        if (loc != NULL && strcmp(loc, oldloc) == 0) {
+            loc = NULL;
+        }
+
+        if (loc != NULL) {
+            /* Only set the locale temporarily the LC_CTYPE locale
+               to the LC_MONETARY locale if the two locales are different and
+               at least one string is non-ASCII. */
+            setlocale(LC_CTYPE, loc);
+        }
+    }
+
+    int res = -1;
+
+#define RESULT_STRING(ATTR) \
+    do { \
+        PyObject *obj; \
+        obj = PyUnicode_DecodeLocale(lc->ATTR, NULL); \
+        if (obj == NULL) { \
+            goto done; \
+        } \
+        if (PyDict_SetItemString(dict, Py_STRINGIFY(ATTR), obj) < 0) { \
+            Py_DECREF(obj); \
+            goto done; \
+        } \
+        Py_DECREF(obj); \
+    } while (0)
+
+    RESULT_STRING(int_curr_symbol);
+    RESULT_STRING(currency_symbol);
+    RESULT_STRING(mon_decimal_point);
+    RESULT_STRING(mon_thousands_sep);
+#undef RESULT_STRING
+
+    res = 0;
+
+done:
+    if (loc != NULL) {
+        setlocale(LC_CTYPE, oldloc);
+    }
+    PyMem_Free(oldloc);
+    return res;
+}
+
 PyDoc_STRVAR(localeconv__doc__,
 "() -> dict. Returns numeric and monetary locale-specific parameters.");
 
@@ -172,11 +241,10 @@ PyLocale_localeconv(PyObject* self)
         RESULT(#i, x); \
     } while (0)
 
-    /* Monetary information */
-    RESULT_STRING(int_curr_symbol);
-    RESULT_STRING(currency_symbol);
-    RESULT_STRING(mon_decimal_point);
-    RESULT_STRING(mon_thousands_sep);
+    /* Monetary information: LC_MONETARY encoding */
+    if (locale_decode_monetary(result, l) < 0) {
+        goto failed;
+    }
     x = copy_grouping(l->mon_grouping);
     RESULT("mon_grouping", x);
 
@@ -191,7 +259,7 @@ PyLocale_localeconv(PyObject* self)
     RESULT_INT(p_sign_posn);
     RESULT_INT(n_sign_posn);
 
-    /* Numeric information */
+    /* Numeric information: LC_NUMERIC encoding */
     PyObject *decimal_point, *thousands_sep;
     const char *grouping;
     if (_Py_GetLocaleconvNumeric(&decimal_point,
@@ -221,6 +289,10 @@ PyLocale_localeconv(PyObject* self)
   failed:
     Py_DECREF(result);
     return NULL;
+
+#undef RESULT
+#undef RESULT_STRING
+#undef RESULT_INT
 }
 
 #if defined(HAVE_WCSCOLL)
index 7b501d8..bb7a7ec 100644 (file)
@@ -108,7 +108,7 @@ catch_lzma_error(lzma_ret lzret)
 static void*
 PyLzma_Malloc(void *opaque, size_t items, size_t size)
 {
-    if (items > (size_t)PY_SSIZE_T_MAX / size)
+    if (size != 0 && items > (size_t)PY_SSIZE_T_MAX / size)
         return NULL;
     /* PyMem_Malloc() cannot be used:
        the GIL is not held when lzma_code() is called */
index 0092b13..0cc46b5 100644 (file)
@@ -449,8 +449,9 @@ semlock_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 
     if (!unlink) {
         name_copy = PyMem_Malloc(strlen(name) + 1);
-        if (name_copy == NULL)
-            goto failure;
+        if (name_copy == NULL) {
+            return PyErr_NoMemory();
+        }
         strcpy(name_copy, name);
     }
 
@@ -473,7 +474,9 @@ semlock_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
     if (handle != SEM_FAILED)
         SEM_CLOSE(handle);
     PyMem_Free(name_copy);
-    _PyMp_SetError(NULL, MP_STANDARD_ERROR);
+    if (!PyErr_Occurred()) {
+        _PyMp_SetError(NULL, MP_STANDARD_ERROR);
+    }
     return NULL;
 }
 
index f2fd656..51daa1f 100644 (file)
@@ -1583,6 +1583,7 @@ methodcaller_repr(methodcallerobject *mc)
                 goto done;
             if (i >= numtotalargs) {
                 i = -1;
+                Py_DECREF(onerepr);
                 break;
             }
             PyTuple_SET_ITEM(argreprs, i, onerepr);
index ba8962d..60ef921 100644 (file)
@@ -207,19 +207,15 @@ _Pickle_ClearState(PickleState *st)
 static int
 _Pickle_InitState(PickleState *st)
 {
-    PyObject *builtins;
     PyObject *copyreg = NULL;
     PyObject *compat_pickle = NULL;
     PyObject *codecs = NULL;
     PyObject *functools = NULL;
+    _Py_IDENTIFIER(getattr);
 
-    builtins = PyEval_GetBuiltins();
-    if (builtins == NULL)
-        goto error;
-    st->getattr = PyDict_GetItemString(builtins, "getattr");
+    st->getattr = _PyEval_GetBuiltinId(&PyId_getattr);
     if (st->getattr == NULL)
         goto error;
-    Py_INCREF(st->getattr);
 
     copyreg = PyImport_ImportModule("copyreg");
     if (!copyreg)
@@ -3838,7 +3834,10 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj)
 
         if (obj != NULL) {
             obj_class = get_class(obj);
-            p = obj_class != cls;    /* true iff a problem */
+            if (obj_class == NULL) {
+                return -1;
+            }
+            p = obj_class != cls;
             Py_DECREF(obj_class);
             if (p) {
                 PyErr_SetString(st->PicklingError, "args[0] from "
@@ -4563,13 +4562,13 @@ PicklerMemoProxy_New(PicklerObject *pickler)
 /*****************************************************************************/
 
 static PyObject *
-Pickler_get_memo(PicklerObject *self)
+Pickler_get_memo(PicklerObject *self, void *Py_UNUSED(ignored))
 {
     return PicklerMemoProxy_New(self);
 }
 
 static int
-Pickler_set_memo(PicklerObject *self, PyObject *obj)
+Pickler_set_memo(PicklerObject *self, PyObject *obj, void *Py_UNUSED(ignored))
 {
     PyMemoTable *new_memo = NULL;
 
@@ -4614,7 +4613,7 @@ Pickler_set_memo(PicklerObject *self, PyObject *obj)
     }
     else {
         PyErr_Format(PyExc_TypeError,
-                     "'memo' attribute must be a PicklerMemoProxy object"
+                     "'memo' attribute must be a PicklerMemoProxy object "
                      "or dict, not %.200s", Py_TYPE(obj)->tp_name);
         return -1;
     }
@@ -4631,7 +4630,7 @@ Pickler_set_memo(PicklerObject *self, PyObject *obj)
 }
 
 static PyObject *
-Pickler_get_persid(PicklerObject *self)
+Pickler_get_persid(PicklerObject *self, void *Py_UNUSED(ignored))
 {
     if (self->pers_func == NULL) {
         PyErr_SetString(PyExc_AttributeError, "persistent_id");
@@ -4641,7 +4640,7 @@ Pickler_get_persid(PicklerObject *self)
 }
 
 static int
-Pickler_set_persid(PicklerObject *self, PyObject *value)
+Pickler_set_persid(PicklerObject *self, PyObject *value, void *Py_UNUSED(ignored))
 {
     if (value == NULL) {
         PyErr_SetString(PyExc_TypeError,
@@ -6999,13 +6998,13 @@ UnpicklerMemoProxy_New(UnpicklerObject *unpickler)
 
 
 static PyObject *
-Unpickler_get_memo(UnpicklerObject *self)
+Unpickler_get_memo(UnpicklerObject *self, void *Py_UNUSED(ignored))
 {
     return UnpicklerMemoProxy_New(self);
 }
 
 static int
-Unpickler_set_memo(UnpicklerObject *self, PyObject *obj)
+Unpickler_set_memo(UnpicklerObject *self, PyObject *obj, void *Py_UNUSED(ignored))
 {
     PyObject **new_memo;
     size_t new_memo_size = 0;
@@ -7060,7 +7059,7 @@ Unpickler_set_memo(UnpicklerObject *self, PyObject *obj)
     }
     else {
         PyErr_Format(PyExc_TypeError,
-                     "'memo' attribute must be an UnpicklerMemoProxy object"
+                     "'memo' attribute must be an UnpicklerMemoProxy object "
                      "or dict, not %.200s", Py_TYPE(obj)->tp_name);
         return -1;
     }
@@ -7082,7 +7081,7 @@ Unpickler_set_memo(UnpicklerObject *self, PyObject *obj)
 }
 
 static PyObject *
-Unpickler_get_persload(UnpicklerObject *self)
+Unpickler_get_persload(UnpicklerObject *self, void *Py_UNUSED(ignored))
 {
     if (self->pers_func == NULL) {
         PyErr_SetString(PyExc_AttributeError, "persistent_load");
@@ -7092,7 +7091,7 @@ Unpickler_get_persload(UnpicklerObject *self)
 }
 
 static int
-Unpickler_set_persload(UnpicklerObject *self, PyObject *value)
+Unpickler_set_persload(UnpicklerObject *self, PyObject *value, void *Py_UNUSED(ignored))
 {
     if (value == NULL) {
         PyErr_SetString(PyExc_TypeError,
index dd69b9e..851aa97 100644 (file)
 #include <dirent.h>
 #endif
 
+#ifdef _Py_MEMORY_SANITIZER
+# include <sanitizer/msan_interface.h>
+#endif
+
 #if defined(__ANDROID__) && __ANDROID_API__ < 21 && !defined(SYS_getdents64)
 # include <sys/linux-syscalls.h>
 # define SYS_getdents64  __NR_getdents64
@@ -287,6 +291,9 @@ _close_open_fds_safe(int start_fd, PyObject* py_fds_to_keep)
                                 sizeof(buffer))) > 0) {
             struct linux_dirent64 *entry;
             int offset;
+#ifdef _Py_MEMORY_SANITIZER
+            __msan_unpoison(buffer, bytes);
+#endif
             for (offset = 0; offset < bytes; offset += entry->d_reclen) {
                 int fd;
                 entry = (struct linux_dirent64 *)(buffer + offset);
index 65f173c..351317e 100644 (file)
@@ -55,7 +55,7 @@ static const char * const begin_statements[] = {
     NULL
 };
 
-static int pysqlite_connection_set_isolation_level(pysqlite_Connection* self, PyObject* isolation_level);
+static int pysqlite_connection_set_isolation_level(pysqlite_Connection* self, PyObject* isolation_level, void *Py_UNUSED(ignored));
 static void _pysqlite_drop_unused_cursor_references(pysqlite_Connection* self);
 
 
@@ -147,7 +147,7 @@ int pysqlite_connection_init(pysqlite_Connection* self, PyObject* args, PyObject
         Py_INCREF(isolation_level);
     }
     Py_CLEAR(self->isolation_level);
-    if (pysqlite_connection_set_isolation_level(self, isolation_level) < 0) {
+    if (pysqlite_connection_set_isolation_level(self, isolation_level, NULL) < 0) {
         Py_DECREF(isolation_level);
         return -1;
     }
@@ -827,18 +827,17 @@ PyObject* pysqlite_connection_create_function(pysqlite_Connection* self, PyObjec
         return NULL;
     }
 
+    if (PyDict_SetItem(self->function_pinboard, func, Py_None) == -1) {
+        return NULL;
+    }
     rc = sqlite3_create_function(self->db, name, narg, SQLITE_UTF8, (void*)func, _pysqlite_func_callback, NULL, NULL);
 
     if (rc != SQLITE_OK) {
         /* Workaround for SQLite bug: no error code or string is available here */
         PyErr_SetString(pysqlite_OperationalError, "Error creating function");
         return NULL;
-    } else {
-        if (PyDict_SetItem(self->function_pinboard, func, Py_None) == -1)
-            return NULL;
-
-        Py_RETURN_NONE;
     }
+    Py_RETURN_NONE;
 }
 
 PyObject* pysqlite_connection_create_aggregate(pysqlite_Connection* self, PyObject* args, PyObject* kwargs)
@@ -859,17 +858,16 @@ PyObject* pysqlite_connection_create_aggregate(pysqlite_Connection* self, PyObje
         return NULL;
     }
 
+    if (PyDict_SetItem(self->function_pinboard, aggregate_class, Py_None) == -1) {
+        return NULL;
+    }
     rc = sqlite3_create_function(self->db, name, n_arg, SQLITE_UTF8, (void*)aggregate_class, 0, &_pysqlite_step_callback, &_pysqlite_final_callback);
     if (rc != SQLITE_OK) {
         /* Workaround for SQLite bug: no error code or string is available here */
         PyErr_SetString(pysqlite_OperationalError, "Error creating aggregate");
         return NULL;
-    } else {
-        if (PyDict_SetItem(self->function_pinboard, aggregate_class, Py_None) == -1)
-            return NULL;
-
-        Py_RETURN_NONE;
     }
+    Py_RETURN_NONE;
 }
 
 static int _authorizer_callback(void* user_arg, int action, const char* arg1, const char* arg2 , const char* dbname, const char* access_attempt_source)
@@ -982,17 +980,15 @@ static PyObject* pysqlite_connection_set_authorizer(pysqlite_Connection* self, P
         return NULL;
     }
 
+    if (PyDict_SetItem(self->function_pinboard, authorizer_cb, Py_None) == -1) {
+        return NULL;
+    }
     rc = sqlite3_set_authorizer(self->db, _authorizer_callback, (void*)authorizer_cb);
-
     if (rc != SQLITE_OK) {
         PyErr_SetString(pysqlite_OperationalError, "Error setting authorizer callback");
         return NULL;
-    } else {
-        if (PyDict_SetItem(self->function_pinboard, authorizer_cb, Py_None) == -1)
-            return NULL;
-
-        Py_RETURN_NONE;
     }
+    Py_RETURN_NONE;
 }
 
 static PyObject* pysqlite_connection_set_progress_handler(pysqlite_Connection* self, PyObject* args, PyObject* kwargs)
@@ -1015,9 +1011,9 @@ static PyObject* pysqlite_connection_set_progress_handler(pysqlite_Connection* s
         /* None clears the progress handler previously set */
         sqlite3_progress_handler(self->db, 0, 0, (void*)0);
     } else {
-        sqlite3_progress_handler(self->db, n, _progress_handler, progress_handler);
         if (PyDict_SetItem(self->function_pinboard, progress_handler, Py_None) == -1)
             return NULL;
+        sqlite3_progress_handler(self->db, n, _progress_handler, progress_handler);
     }
 
     Py_RETURN_NONE;
@@ -1139,7 +1135,8 @@ static PyObject* pysqlite_connection_get_in_transaction(pysqlite_Connection* sel
     Py_RETURN_FALSE;
 }
 
-static int pysqlite_connection_set_isolation_level(pysqlite_Connection* self, PyObject* isolation_level)
+static int
+pysqlite_connection_set_isolation_level(pysqlite_Connection* self, PyObject* isolation_level, void *Py_UNUSED(ignored))
 {
     if (isolation_level == Py_None) {
         PyObject *res = pysqlite_connection_commit(self, NULL);
index ec2c788..122bee0 100644 (file)
@@ -150,7 +150,8 @@ PyObject* pysqlite_row_subscript(pysqlite_Row* self, PyObject* idx)
     }
 }
 
-Py_ssize_t pysqlite_row_length(pysqlite_Row* self, PyObject* args, PyObject* kwargs)
+static Py_ssize_t
+pysqlite_row_length(pysqlite_Row* self)
 {
     return PyTuple_GET_SIZE(self->data);
 }
index b6be6f6..d2ea62d 100644 (file)
@@ -1312,7 +1312,7 @@ PyDoc_STRVAR(pattern_doc, "Compiled regular expression object.");
 
 /* PatternObject's 'groupindex' method. */
 static PyObject *
-pattern_groupindex(PatternObject *self)
+pattern_groupindex(PatternObject *self, void *Py_UNUSED(ignored))
 {
     if (self->groupindex == NULL)
         return PyDict_New();
@@ -2279,7 +2279,7 @@ PyDoc_STRVAR(match_group_doc,
     For 0 returns the entire match.");
 
 static PyObject *
-match_lastindex_get(MatchObject *self)
+match_lastindex_get(MatchObject *self, void *Py_UNUSED(ignored))
 {
     if (self->lastindex >= 0)
         return PyLong_FromSsize_t(self->lastindex);
@@ -2287,7 +2287,7 @@ match_lastindex_get(MatchObject *self)
 }
 
 static PyObject *
-match_lastgroup_get(MatchObject *self)
+match_lastgroup_get(MatchObject *self, void *Py_UNUSED(ignored))
 {
     if (self->pattern->indexgroup &&
         self->lastindex >= 0 &&
@@ -2302,7 +2302,7 @@ match_lastgroup_get(MatchObject *self)
 }
 
 static PyObject *
-match_regs_get(MatchObject *self)
+match_regs_get(MatchObject *self, void *Py_UNUSED(ignored))
 {
     if (self->regs) {
         Py_INCREF(self->regs);
index 4253e2a..310b38b 100644 (file)
@@ -912,6 +912,11 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
     PySSL_BEGIN_ALLOW_THREADS
     self->ssl = SSL_new(ctx);
     PySSL_END_ALLOW_THREADS
+    if (self->ssl == NULL) {
+        Py_DECREF(self);
+        _setSSLError(NULL, 0, __FILE__, __LINE__);
+        return NULL;
+    }
     SSL_set_app_data(self->ssl, self);
     if (sock) {
         SSL_set_fd(self->ssl, Py_SAFE_DOWNCAST(sock->sock_fd, SOCKET_T, int));
@@ -1241,6 +1246,10 @@ _get_peer_alt_names (X509 *certificate) {
 
     /* get a memory buffer */
     biobuf = BIO_new(BIO_s_mem());
+    if (biobuf == NULL) {
+        PyErr_SetString(PySSLErrorObject, "failed to allocate BIO");
+        return NULL;
+    }
 
     names = (GENERAL_NAMES *)X509_get_ext_d2i(
         certificate, NID_subject_alt_name, NULL, NULL);
@@ -1593,6 +1602,10 @@ _decode_certificate(X509 *certificate) {
 
     /* get a memory buffer */
     biobuf = BIO_new(BIO_s_mem());
+    if (biobuf == NULL) {
+        PyErr_SetString(PySSLErrorObject, "failed to allocate BIO");
+        goto fail0;
+    }
 
     (void) BIO_reset(biobuf);
     serialNumber = X509_get_serialNumber(certificate);
@@ -4711,12 +4724,17 @@ _ssl_MemoryBIO_read_impl(PySSLMemoryBIO *self, int len)
         return result;
 
     nbytes = BIO_read(self->bio, PyBytes_AS_STRING(result), len);
-    /* There should never be any short reads but check anyway. */
-    if ((nbytes < len) && (_PyBytes_Resize(&result, len) < 0)) {
+    if (nbytes < 0) {
         Py_DECREF(result);
+        _setSSLError(NULL, 0, __FILE__, __LINE__);
         return NULL;
     }
 
+    /* There should never be any short reads but check anyway. */
+    if (nbytes < len) {
+        _PyBytes_Resize(&result, nbytes);
+    }
+
     return result;
 }
 
index 669794d..a16b96d 100644 (file)
@@ -1531,7 +1531,7 @@ ndarray_getbuf(NDArrayObject *self, Py_buffer *view, int flags)
     return 0;
 }
 
-static int
+static void
 ndarray_releasebuf(NDArrayObject *self, Py_buffer *view)
 {
     if (!ND_IS_CONSUMER(self)) {
@@ -1539,8 +1539,6 @@ ndarray_releasebuf(NDArrayObject *self, Py_buffer *view)
         if (--ndbuf->exports == 0 && ndbuf != self->head)
             ndbuf_delete(self, ndbuf);
     }
-
-    return 0;
 }
 
 static PyBufferProcs ndarray_as_buffer = {
index 174b767..eb050ff 100644 (file)
@@ -943,7 +943,7 @@ test_buildvalue_N_error(const char *fmt)
 }
 
 static PyObject *
-test_buildvalue_N(PyObject *self, PyObject *noargs)
+test_buildvalue_N(PyObject *self, PyObject *Py_UNUSED(ignored))
 {
     PyObject *arg, *res;
 
@@ -2453,7 +2453,7 @@ pending_threadfunc(PyObject *self, PyObject *arg)
 
 /* Some tests of PyUnicode_FromFormat().  This needs more tests. */
 static PyObject *
-test_string_from_format(PyObject *self, PyObject *args)
+test_string_from_format(PyObject *self, PyObject *Py_UNUSED(ignored))
 {
     PyObject *result;
     char *msg;
@@ -2593,7 +2593,7 @@ typedef struct {
 } known_capsule;
 
 static PyObject *
-test_capsule(PyObject *self, PyObject *args)
+test_capsule(PyObject *self, PyObject *Py_UNUSED(ignored))
 {
     PyObject *object;
     const char *error = NULL;
@@ -2964,7 +2964,7 @@ make_memoryview_from_NULL_pointer(PyObject *self)
 }
 
 static PyObject *
-test_from_contiguous(PyObject* self, PyObject *noargs)
+test_from_contiguous(PyObject* self, PyObject *Py_UNUSED(ignored))
 {
     int data[9] = {-1,-1,-1,-1,-1,-1,-1,-1,-1};
     int init[5] = {0, 1, 2, 3, 4};
@@ -3017,7 +3017,7 @@ test_from_contiguous(PyObject* self, PyObject *noargs)
 extern PyTypeObject _PyBytesIOBuffer_Type;
 
 static PyObject *
-test_pep3118_obsolete_write_locks(PyObject* self, PyObject *noargs)
+test_pep3118_obsolete_write_locks(PyObject* self, PyObject *Py_UNUSED(ignored))
 {
     PyTypeObject *type = &_PyBytesIOBuffer_Type;
     PyObject *b;
@@ -4551,6 +4551,31 @@ new_hamt(PyObject *self, PyObject *args)
 }
 
 
+static PyObject *
+get_global_config(PyObject *self, PyObject *Py_UNUSED(args))
+{
+    return _Py_GetGlobalVariablesAsDict();
+}
+
+
+static PyObject *
+get_core_config(PyObject *self, PyObject *Py_UNUSED(args))
+{
+    PyInterpreterState *interp = PyThreadState_GET()->interp;
+    const _PyCoreConfig *config = &interp->core_config;
+    return _PyCoreConfig_AsDict(config);
+}
+
+
+static PyObject *
+get_main_config(PyObject *self, PyObject *Py_UNUSED(args))
+{
+    PyInterpreterState *interp = PyThreadState_GET()->interp;
+    const _PyMainInterpreterConfig *config = &interp->config;
+    return _PyMainInterpreterConfig_AsDict(config);
+}
+
+
 static PyMethodDef TestMethods[] = {
     {"raise_exception",         raise_exception,                 METH_VARARGS},
     {"raise_memoryerror",   (PyCFunction)raise_memoryerror,  METH_NOARGS},
@@ -4777,6 +4802,9 @@ static PyMethodDef TestMethods[] = {
     {"get_mapping_items", get_mapping_items, METH_O},
     {"test_pythread_tss_key_state", test_pythread_tss_key_state, METH_VARARGS},
     {"hamt", new_hamt, METH_NOARGS},
+    {"get_global_config", get_global_config, METH_NOARGS},
+    {"get_core_config", get_core_config, METH_NOARGS},
+    {"get_main_config", get_main_config, METH_NOARGS},
     {NULL, NULL} /* sentinel */
 };
 
index 16d3191..2a6b893 100644 (file)
@@ -777,9 +777,11 @@ local_clear(localobject *self)
         for(tstate = PyInterpreterState_ThreadHead(tstate->interp);
             tstate;
             tstate = PyThreadState_Next(tstate))
-            if (tstate->dict &&
-                PyDict_GetItem(tstate->dict, self->key))
-                PyDict_DelItem(tstate->dict, self->key);
+            if (tstate->dict && PyDict_GetItem(tstate->dict, self->key)) {
+                if (PyDict_DelItem(tstate->dict, self->key)) {
+                    PyErr_Clear();
+                }
+            }
     }
     return 0;
 }
index 93d4dbc..a96924c 100644 (file)
@@ -842,7 +842,7 @@ PyTclObject_string(PyTclObject *self, void *ignored)
 }
 
 static PyObject *
-PyTclObject_str(PyTclObject *self, void *ignored)
+PyTclObject_str(PyTclObject *self)
 {
     if (self->string) {
         Py_INCREF(self->string);
@@ -855,7 +855,7 @@ PyTclObject_str(PyTclObject *self, void *ignored)
 static PyObject *
 PyTclObject_repr(PyTclObject *self)
 {
-    PyObject *repr, *str = PyTclObject_str(self, NULL);
+    PyObject *repr, *str = PyTclObject_str(self);
     if (str == NULL)
         return NULL;
     repr = PyUnicode_FromFormat("<%s object: %R>",
@@ -1101,9 +1101,7 @@ AsObj(PyObject *value)
     }
 
     if (PyTclObject_Check(value)) {
-        Tcl_Obj *v = ((PyTclObject*)value)->value;
-        Tcl_IncrRefCount(v);
-        return v;
+        return ((PyTclObject*)value)->value;
     }
 
     {
index e07022c..7f19c55 100644 (file)
@@ -1490,6 +1490,12 @@ _PyMem_DumpTraceback(int fd, const void *ptr)
     traceback_t *traceback;
     int i;
 
+    if (!tracemalloc_config.tracing) {
+        PUTS(fd, "Enable tracemalloc to get the memory block "
+                 "allocation traceback\n\n");
+        return;
+    }
+
     traceback = tracemalloc_get_traceback(DEFAULT_DOMAIN, (uintptr_t)ptr);
     if (traceback == NULL)
         return;
index 900c374..7b4a4a3 100644 (file)
@@ -1544,12 +1544,18 @@ array_array_fromlist(arrayobject *self, PyObject *list)
         if (array_resize(self, old_size + n) == -1)
             return NULL;
         for (i = 0; i < n; i++) {
-            PyObject *v = PyList_GetItem(list, i);
+            PyObject *v = PyList_GET_ITEM(list, i);
             if ((*self->ob_descr->setitem)(self,
                             Py_SIZE(self) - n + i, v) != 0) {
                 array_resize(self, old_size);
                 return NULL;
             }
+            if (n != PyList_GET_SIZE(list)) {
+                PyErr_SetString(PyExc_RuntimeError,
+                                "list changed size during iteration");
+                array_resize(self, old_size);
+                return NULL;
+            }
         }
     }
     Py_RETURN_NONE;
@@ -1574,8 +1580,7 @@ array_array_tolist_impl(arrayobject *self)
         PyObject *v = getarrayitem((PyObject *)self, i);
         if (v == NULL)
             goto error;
-        if (PyList_SetItem(list, i, v) < 0)
-            goto error;
+        PyList_SET_ITEM(list, i, v);
     }
     return list;
 
index 2c7788a..3a33295 100644 (file)
@@ -40,7 +40,7 @@ ENCODER(cp932)
             if (c == 0xf8f0)
                 OUTBYTE1(0xa0);
             else
-                OUTBYTE1(c - 0xfef1 + 0xfd);
+                OUTBYTE1(c - 0xf8f1 + 0xfd);
             NEXT(1, 1);
             continue;
         }
index 22172b0..4d0aaf3 100644 (file)
@@ -108,7 +108,7 @@ call_error_callback(PyObject *errors, PyObject *exc)
 }
 
 static PyObject *
-codecctx_errors_get(MultibyteStatefulCodecContext *self)
+codecctx_errors_get(MultibyteStatefulCodecContext *self, void *Py_UNUSED(ignored))
 {
     const char *errors;
 
index 850d76b..fa1a0d6 100644 (file)
@@ -24,7 +24,7 @@ PyDoc_STRVAR(MD5Type_digest__doc__,
 "digest($self, /)\n"
 "--\n"
 "\n"
-"Return the digest value as a string of binary data.");
+"Return the digest value as a bytes object.");
 
 #define MD5TYPE_DIGEST_METHODDEF    \
     {"digest", (PyCFunction)MD5Type_digest, METH_NOARGS, MD5Type_digest__doc__},
@@ -94,4 +94,4 @@ _md5_md5(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=50a95670913de8fb input=a9049054013a1b77]*/
+/*[clinic end generated code: output=72aa003c308e26cf input=a9049054013a1b77]*/
index ab1f753..3230cd7 100644 (file)
@@ -9,7 +9,7 @@ PyDoc_STRVAR(os_stat__doc__,
 "Perform a stat system call on the given path.\n"
 "\n"
 "  path\n"
-"    Path to be examined; can be string, bytes, path-like object or\n"
+"    Path to be examined; can be string, bytes, path-like object or\n"
 "    open-file-descriptor int.\n"
 "  dir_fd\n"
 "    If not None, it should be a file descriptor open to a directory,\n"
@@ -101,7 +101,7 @@ PyDoc_STRVAR(os_access__doc__,
 "Use the real uid/gid to test for access to a path.\n"
 "\n"
 "  path\n"
-"    Path to be tested; can be string or bytes\n"
+"    Path to be tested; can be string, bytes, or a path-like object.\n"
 "  mode\n"
 "    Operating-system mode bitfield.  Can be F_OK to test existence,\n"
 "    or the inclusive-OR of R_OK, W_OK, and X_OK.\n"
@@ -304,7 +304,7 @@ PyDoc_STRVAR(os_chmod__doc__,
 "Change the access permissions of a file.\n"
 "\n"
 "  path\n"
-"    Path to be modified.  May always be specified as a str or bytes.\n"
+"    Path to be modified.  May always be specified as a str, bytes, or a path-like object.\n"
 "    On some platforms, path may also be specified as an open file descriptor.\n"
 "    If this functionality is unavailable, using it raises an exception.\n"
 "  mode\n"
@@ -655,7 +655,7 @@ PyDoc_STRVAR(os_chown__doc__,
 "Change the owner and group id of path to the numeric uid and gid.\\\n"
 "\n"
 "  path\n"
-"    Path to be examined; can be string, bytes, or open-file-descriptor int.\n"
+"    Path to be examined; can be string, bytes, a path-like object, or open-file-descriptor int.\n"
 "  dir_fd\n"
 "    If not None, it should be a file descriptor open to a directory,\n"
 "    and path should be relative; path will then be relative to that\n"
@@ -889,7 +889,7 @@ PyDoc_STRVAR(os_listdir__doc__,
 "\n"
 "Return a list containing the names of the files in the directory.\n"
 "\n"
-"path can be specified as either str or bytes.  If path is bytes,\n"
+"path can be specified as either str, bytes, or a path-like object.  If path is bytes,\n"
 "  the filenames returned will also be bytes; in all other circumstances\n"
 "  the filenames returned will be str.\n"
 "If path is None, uses the path=\'.\'.\n"
@@ -5406,7 +5406,7 @@ PyDoc_STRVAR(os_getxattr__doc__,
 "\n"
 "Return the value of extended attribute attribute on path.\n"
 "\n"
-"path may be either a string or an open file descriptor.\n"
+"path may be either a string, a path-like object, or an open file descriptor.\n"
 "If follow_symlinks is False, and the last element of the path is a symbolic\n"
 "  link, getxattr will examine the symbolic link itself instead of the file\n"
 "  the link points to.");
@@ -5454,7 +5454,7 @@ PyDoc_STRVAR(os_setxattr__doc__,
 "\n"
 "Set extended attribute attribute on path to value.\n"
 "\n"
-"path may be either a string or an open file descriptor.\n"
+"path may be either a string, a path-like object,  or an open file descriptor.\n"
 "If follow_symlinks is False, and the last element of the path is a symbolic\n"
 "  link, setxattr will modify the symbolic link itself instead of the file\n"
 "  the link points to.");
@@ -5507,7 +5507,7 @@ PyDoc_STRVAR(os_removexattr__doc__,
 "\n"
 "Remove extended attribute attribute on path.\n"
 "\n"
-"path may be either a string or an open file descriptor.\n"
+"path may be either a string, a path-like object, or an open file descriptor.\n"
 "If follow_symlinks is False, and the last element of the path is a symbolic\n"
 "  link, removexattr will modify the symbolic link itself instead of the file\n"
 "  the link points to.");
@@ -5554,7 +5554,7 @@ PyDoc_STRVAR(os_listxattr__doc__,
 "\n"
 "Return a list of extended attributes on path.\n"
 "\n"
-"path may be either None, a string, or an open file descriptor.\n"
+"path may be either None, a string, a path-like object, or an open file descriptor.\n"
 "if path is None, listxattr will examine the current directory.\n"
 "If follow_symlinks is False, and the last element of the path is a symbolic\n"
 "  link, listxattr will examine the symbolic link itself instead of the file\n"
@@ -5940,7 +5940,7 @@ PyDoc_STRVAR(os_scandir__doc__,
 "\n"
 "Return an iterator of DirEntry objects for given path.\n"
 "\n"
-"path can be specified as either str, bytes or path-like object.  If path\n"
+"path can be specified as either str, bytes, or a path-like object.  If path\n"
 "is bytes, the names of yielded DirEntry objects will also be bytes; in\n"
 "all other circumstances they will be str.\n"
 "\n"
@@ -6537,4 +6537,4 @@ exit:
 #ifndef OS_GETRANDOM_METHODDEF
     #define OS_GETRANDOM_METHODDEF
 #endif /* !defined(OS_GETRANDOM_METHODDEF) */
-/*[clinic end generated code: output=c966c821d557b7c0 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=c6ca6ad4afa64454 input=a9049054013a1b77]*/
index be29355..c84fcba 100644 (file)
@@ -24,7 +24,7 @@ PyDoc_STRVAR(SHA1Type_digest__doc__,
 "digest($self, /)\n"
 "--\n"
 "\n"
-"Return the digest value as a string of binary data.");
+"Return the digest value as a bytes object.");
 
 #define SHA1TYPE_DIGEST_METHODDEF    \
     {"digest", (PyCFunction)SHA1Type_digest, METH_NOARGS, SHA1Type_digest__doc__},
@@ -94,4 +94,4 @@ _sha1_sha1(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=9ee2aec7bb2b9e72 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=81d2424c0585bfd4 input=a9049054013a1b77]*/
index 9de5bd0..45f78c8 100644 (file)
@@ -24,7 +24,7 @@ PyDoc_STRVAR(SHA256Type_digest__doc__,
 "digest($self, /)\n"
 "--\n"
 "\n"
-"Return the digest value as a string of binary data.");
+"Return the digest value as a bytes object.");
 
 #define SHA256TYPE_DIGEST_METHODDEF    \
     {"digest", (PyCFunction)SHA256Type_digest, METH_NOARGS, SHA256Type_digest__doc__},
@@ -124,4 +124,4 @@ _sha256_sha224(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=4b90199bc9f7cc88 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=0086286cffcbc31c input=a9049054013a1b77]*/
index ebb510e..9d3a7c9 100644 (file)
@@ -24,7 +24,7 @@ PyDoc_STRVAR(SHA512Type_digest__doc__,
 "digest($self, /)\n"
 "--\n"
 "\n"
-"Return the digest value as a string of binary data.");
+"Return the digest value as a bytes object.");
 
 #define SHA512TYPE_DIGEST_METHODDEF    \
     {"digest", (PyCFunction)SHA512Type_digest, METH_NOARGS, SHA512Type_digest__doc__},
@@ -124,4 +124,4 @@ _sha512_sha384(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=f963a543bf3c72e8 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=fcc3306fb6672222 input=a9049054013a1b77]*/
index 629483a..2d96b4f 100644 (file)
 
 /* External API definitions */
 
+/* Namespace external symbols to allow multiple libexpat version to
+   co-exist. */
+#include "pyexpatns.h"
+
 #if defined(_MSC_EXTENSIONS) && !defined(__BEOS__) && !defined(__CYGWIN__)
 # define XML_USE_MSC_EXTENSIONS 1
 #endif
index 6b415d8..6371a35 100644 (file)
@@ -30,6 +30,9 @@
    USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
 
+#if !defined(_WIN32) && defined(HAVE_EXPAT_CONFIG_H)
+#  include <pyconfig.h>
+#endif
 #include <stddef.h>
 #include <string.h>  /* memcpy */
 
index f1da9f7..cf24c9b 100644 (file)
@@ -1369,7 +1369,7 @@ void _PyFaulthandler_Fini(void)
 #ifdef HAVE_SIGALTSTACK
     if (stack.ss_sp != NULL) {
         /* Fetch the current alt stack */
-        stack_t current_stack;
+        stack_t current_stack = {};
         if (sigaltstack(NULL, &current_stack) == 0) {
             if (current_stack.ss_sp == stack.ss_sp) {
                 /* The current alt stack is the one that we installed.
index 0baaa83..1638056 100644 (file)
@@ -64,6 +64,7 @@ fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg)
     char *str;
     Py_ssize_t len;
     char buf[1024];
+    int async_err = 0;
 
     if (arg != NULL) {
         int parse_result;
@@ -75,12 +76,13 @@ fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg)
                 return NULL;
             }
             memcpy(buf, str, len);
-            Py_BEGIN_ALLOW_THREADS
-            ret = fcntl(fd, code, buf);
-            Py_END_ALLOW_THREADS
+            do {
+                Py_BEGIN_ALLOW_THREADS
+                ret = fcntl(fd, code, buf);
+                Py_END_ALLOW_THREADS
+            } while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
             if (ret < 0) {
-                PyErr_SetFromErrno(PyExc_OSError);
-                return NULL;
+                return !async_err ? PyErr_SetFromErrno(PyExc_OSError) : NULL;
             }
             return PyBytes_FromStringAndSize(buf, len);
         }
@@ -95,12 +97,13 @@ fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg)
         }
     }
 
-    Py_BEGIN_ALLOW_THREADS
-    ret = fcntl(fd, code, (int)int_arg);
-    Py_END_ALLOW_THREADS
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        ret = fcntl(fd, code, (int)int_arg);
+        Py_END_ALLOW_THREADS
+    } while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
     if (ret < 0) {
-        PyErr_SetFromErrno(PyExc_OSError);
-        return NULL;
+        return !async_err ? PyErr_SetFromErrno(PyExc_OSError) : NULL;
     }
     return PyLong_FromLong((long)ret);
 }
@@ -283,11 +286,14 @@ fcntl_flock_impl(PyObject *module, int fd, int code)
 /*[clinic end generated code: output=84059e2b37d2fc64 input=b70a0a41ca22a8a0]*/
 {
     int ret;
+    int async_err = 0;
 
 #ifdef HAVE_FLOCK
-    Py_BEGIN_ALLOW_THREADS
-    ret = flock(fd, code);
-    Py_END_ALLOW_THREADS
+    do {
+        Py_BEGIN_ALLOW_THREADS
+        ret = flock(fd, code);
+        Py_END_ALLOW_THREADS
+    } while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
 #else
 
 #ifndef LOCK_SH
@@ -310,14 +316,15 @@ fcntl_flock_impl(PyObject *module, int fd, int code)
             return NULL;
         }
         l.l_whence = l.l_start = l.l_len = 0;
-        Py_BEGIN_ALLOW_THREADS
-        ret = fcntl(fd, (code & LOCK_NB) ? F_SETLK : F_SETLKW, &l);
-        Py_END_ALLOW_THREADS
+        do {
+            Py_BEGIN_ALLOW_THREADS
+            ret = fcntl(fd, (code & LOCK_NB) ? F_SETLK : F_SETLKW, &l);
+            Py_END_ALLOW_THREADS
+        } while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
     }
 #endif /* HAVE_FLOCK */
     if (ret < 0) {
-        PyErr_SetFromErrno(PyExc_OSError);
-        return NULL;
+        return !async_err ? PyErr_SetFromErrno(PyExc_OSError) : NULL;
     }
     Py_RETURN_NONE;
 }
@@ -363,6 +370,7 @@ fcntl_lockf_impl(PyObject *module, int fd, int code, PyObject *lenobj,
 /*[clinic end generated code: output=4985e7a172e7461a input=3a5dc01b04371f1a]*/
 {
     int ret;
+    int async_err = 0;
 
 #ifndef LOCK_SH
 #define LOCK_SH         1       /* shared lock */
@@ -407,13 +415,14 @@ fcntl_lockf_impl(PyObject *module, int fd, int code, PyObject *lenobj,
                 return NULL;
         }
         l.l_whence = whence;
-        Py_BEGIN_ALLOW_THREADS
-        ret = fcntl(fd, (code & LOCK_NB) ? F_SETLK : F_SETLKW, &l);
-        Py_END_ALLOW_THREADS
+        do {
+            Py_BEGIN_ALLOW_THREADS
+            ret = fcntl(fd, (code & LOCK_NB) ? F_SETLK : F_SETLKW, &l);
+            Py_END_ALLOW_THREADS
+        } while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
     }
     if (ret < 0) {
-        PyErr_SetFromErrno(PyExc_OSError);
-        return NULL;
+        return !async_err ? PyErr_SetFromErrno(PyExc_OSError) : NULL;
     }
     Py_RETURN_NONE;
 }
index 43e45ef..8a724b6 100644 (file)
@@ -156,7 +156,7 @@ grp_getgrnam_impl(PyObject *module, PyObject *name)
         goto out;
 
     if ((p = getgrnam(name_chars)) == NULL) {
-        PyErr_Format(PyExc_KeyError, "getgrnam(): name not found: %S", name);
+        PyErr_Format(PyExc_KeyError, "getgrnam(): name not found: %R", name);
         goto out;
     }
     retval = mkgrent(p);
index 7771d27..af2c191 100644 (file)
@@ -1206,6 +1206,25 @@ config_init_program_name(_PyCoreConfig *config)
 }
 
 
+static _PyInitError
+config_init_executable(_PyCoreConfig *config)
+{
+    assert(config->executable == NULL);
+
+    /* If Py_SetProgramFullPath() was called, use its value */
+    const wchar_t *program_full_path = _Py_path_config.program_full_path;
+    if (program_full_path != NULL) {
+        config->executable = _PyMem_RawWcsdup(program_full_path);
+        if (config->executable == NULL) {
+            return _Py_INIT_NO_MEMORY();
+        }
+        return _Py_INIT_OK();
+    }
+
+    return _Py_INIT_OK();
+}
+
+
 static void
 pymain_header(_PyMain *pymain)
 {
@@ -1297,7 +1316,7 @@ pymain_init_core_argv(_PyMain *pymain, _PyCoreConfig *config,
 
 
 static PyObject*
-wstrlist_as_pylist(int len, wchar_t **list)
+_Py_wstrlist_as_pylist(int len, wchar_t **list)
 {
     assert(list != NULL || len < 1);
 
@@ -1361,6 +1380,77 @@ pymain_update_sys_path(_PyMain *pymain, PyObject *path0)
 }
 
 
+PyObject *
+_Py_GetGlobalVariablesAsDict(void)
+{
+    PyObject *dict, *obj;
+
+    dict = PyDict_New();
+    if (dict == NULL) {
+        return NULL;
+    }
+
+#define SET_ITEM(KEY, EXPR) \
+        do { \
+            obj = (EXPR); \
+            if (obj == NULL) { \
+                return NULL; \
+            } \
+            int res = PyDict_SetItemString(dict, (KEY), obj); \
+            Py_DECREF(obj); \
+            if (res < 0) { \
+                goto fail; \
+            } \
+        } while (0)
+#define SET_ITEM_INT(VAR) \
+    SET_ITEM(#VAR, PyLong_FromLong(VAR))
+#define FROM_STRING(STR) \
+    ((STR != NULL) ? \
+        PyUnicode_FromString(STR) \
+        : (Py_INCREF(Py_None), Py_None))
+#define SET_ITEM_STR(VAR) \
+    SET_ITEM(#VAR, FROM_STRING(VAR))
+
+    SET_ITEM_STR(Py_FileSystemDefaultEncoding);
+    SET_ITEM_INT(Py_HasFileSystemDefaultEncoding);
+    SET_ITEM_STR(Py_FileSystemDefaultEncodeErrors);
+
+    SET_ITEM_INT(Py_UTF8Mode);
+    SET_ITEM_INT(Py_DebugFlag);
+    SET_ITEM_INT(Py_VerboseFlag);
+    SET_ITEM_INT(Py_QuietFlag);
+    SET_ITEM_INT(Py_InteractiveFlag);
+    SET_ITEM_INT(Py_InspectFlag);
+
+    SET_ITEM_INT(Py_OptimizeFlag);
+    SET_ITEM_INT(Py_NoSiteFlag);
+    SET_ITEM_INT(Py_BytesWarningFlag);
+    SET_ITEM_INT(Py_FrozenFlag);
+    SET_ITEM_INT(Py_IgnoreEnvironmentFlag);
+    SET_ITEM_INT(Py_DontWriteBytecodeFlag);
+    SET_ITEM_INT(Py_NoUserSiteDirectory);
+    SET_ITEM_INT(Py_UnbufferedStdioFlag);
+    SET_ITEM_INT(Py_HashRandomizationFlag);
+    SET_ITEM_INT(Py_IsolatedFlag);
+
+#ifdef MS_WINDOWS
+    SET_ITEM_INT(Py_LegacyWindowsFSEncodingFlag);
+    SET_ITEM_INT(Py_LegacyWindowsStdioFlag);
+#endif
+
+    return dict;
+
+fail:
+    Py_DECREF(dict);
+    return NULL;
+
+#undef FROM_STRING
+#undef SET_ITEM
+#undef SET_ITEM_INT
+#undef SET_ITEM_STR
+}
+
+
 void
 _PyCoreConfig_GetGlobalConfig(_PyCoreConfig *config)
 {
@@ -2101,11 +2191,17 @@ done:
 static void
 config_init_locale(_PyCoreConfig *config)
 {
-    if (config->coerce_c_locale < 0) {
+    /* Test also if coerce_c_locale equals 1: PYTHONCOERCECLOCALE=1 doesn't
+       imply that the C locale is always coerced. It is only coerced if
+       if the LC_CTYPE locale is "C". */
+    if (config->coerce_c_locale != 0) {
         /* The C locale enables the C locale coercion (PEP 538) */
         if (_Py_LegacyLocaleDetected()) {
             config->coerce_c_locale = 1;
         }
+        else {
+            config->coerce_c_locale = 0;
+        }
     }
 
 #ifndef MS_WINDOWS
@@ -2279,7 +2375,14 @@ _PyCoreConfig_Read(_PyCoreConfig *config)
         }
     }
 
-    if (config->utf8_mode < 0 || config->coerce_c_locale < 0) {
+    if (config->executable == NULL) {
+        err = config_init_executable(config);
+        if (_Py_INIT_FAILED(err)) {
+            return err;
+        }
+    }
+
+    if (config->coerce_c_locale != 0 || config->utf8_mode < 0) {
         config_init_locale(config);
     }
 
@@ -2432,6 +2535,95 @@ _PyCoreConfig_Copy(_PyCoreConfig *config, const _PyCoreConfig *config2)
 }
 
 
+PyObject *
+_PyCoreConfig_AsDict(const _PyCoreConfig *config)
+{
+    PyObject *dict, *obj;
+
+    dict = PyDict_New();
+    if (dict == NULL) {
+        return NULL;
+    }
+
+#define SET_ITEM(KEY, EXPR) \
+        do { \
+            obj = (EXPR); \
+            if (obj == NULL) { \
+                return NULL; \
+            } \
+            int res = PyDict_SetItemString(dict, (KEY), obj); \
+            Py_DECREF(obj); \
+            if (res < 0) { \
+                goto fail; \
+            } \
+        } while (0)
+#define FROM_STRING(STR) \
+    ((STR != NULL) ? \
+        PyUnicode_FromString(STR) \
+        : (Py_INCREF(Py_None), Py_None))
+#define SET_ITEM_INT(ATTR) \
+    SET_ITEM(#ATTR, PyLong_FromLong(config->ATTR))
+#define SET_ITEM_UINT(ATTR) \
+    SET_ITEM(#ATTR, PyLong_FromUnsignedLong(config->ATTR))
+#define SET_ITEM_STR(ATTR) \
+    SET_ITEM(#ATTR, FROM_STRING(config->ATTR))
+#define FROM_WSTRING(STR) \
+    ((STR != NULL) ? \
+        PyUnicode_FromWideChar(STR, -1) \
+        : (Py_INCREF(Py_None), Py_None))
+#define SET_ITEM_WSTR(ATTR) \
+    SET_ITEM(#ATTR, FROM_WSTRING(config->ATTR))
+#define SET_ITEM_WSTRLIST(NOPTION, OPTIONS) \
+    SET_ITEM(#OPTIONS, _Py_wstrlist_as_pylist(config->NOPTION, config->OPTIONS))
+
+    SET_ITEM_INT(install_signal_handlers);
+    SET_ITEM_INT(ignore_environment);
+    SET_ITEM_INT(use_hash_seed);
+    SET_ITEM_UINT(hash_seed);
+    SET_ITEM_STR(allocator);
+    SET_ITEM_INT(dev_mode);
+    SET_ITEM_INT(faulthandler);
+    SET_ITEM_INT(tracemalloc);
+    SET_ITEM_INT(import_time);
+    SET_ITEM_INT(show_ref_count);
+    SET_ITEM_INT(show_alloc_count);
+    SET_ITEM_INT(dump_refs);
+    SET_ITEM_INT(malloc_stats);
+    SET_ITEM_INT(coerce_c_locale);
+    SET_ITEM_INT(coerce_c_locale_warn);
+    SET_ITEM_INT(utf8_mode);
+    SET_ITEM_WSTR(program_name);
+    SET_ITEM_WSTRLIST(argc, argv);
+    SET_ITEM_WSTR(program);
+    SET_ITEM_WSTRLIST(nxoption, xoptions);
+    SET_ITEM_WSTRLIST(nwarnoption, warnoptions);
+    SET_ITEM_WSTR(module_search_path_env);
+    SET_ITEM_WSTR(home);
+    SET_ITEM_WSTRLIST(nmodule_search_path, module_search_paths);
+    SET_ITEM_WSTR(executable);
+    SET_ITEM_WSTR(prefix);
+    SET_ITEM_WSTR(base_prefix);
+    SET_ITEM_WSTR(exec_prefix);
+    SET_ITEM_WSTR(base_exec_prefix);
+    SET_ITEM_INT(_disable_importlib);
+
+    return dict;
+
+fail:
+    Py_DECREF(dict);
+    return NULL;
+
+#undef FROM_STRING
+#undef FROM_WSTRING
+#undef SET_ITEM
+#undef SET_ITEM_INT
+#undef SET_ITEM_UINT
+#undef SET_ITEM_STR
+#undef SET_ITEM_WSTR
+#undef SET_ITEM_WSTRLIST
+}
+
+
 void
 _PyMainInterpreterConfig_Clear(_PyMainInterpreterConfig *config)
 {
@@ -2477,7 +2669,8 @@ _PyMainInterpreterConfig_Copy(_PyMainInterpreterConfig *config,
 {
     _PyMainInterpreterConfig_Clear(config);
 
-#define COPY_ATTR(ATTR) \
+#define COPY_ATTR(ATTR) config->ATTR = config2->ATTR
+#define COPY_OBJ_ATTR(ATTR) \
     do { \
         if (config2->ATTR != NULL) { \
             config->ATTR = config_copy_attr(config2->ATTR); \
@@ -2487,20 +2680,77 @@ _PyMainInterpreterConfig_Copy(_PyMainInterpreterConfig *config,
         } \
     } while (0)
 
-    COPY_ATTR(argv);
-    COPY_ATTR(executable);
-    COPY_ATTR(prefix);
-    COPY_ATTR(base_prefix);
-    COPY_ATTR(exec_prefix);
-    COPY_ATTR(base_exec_prefix);
-    COPY_ATTR(warnoptions);
-    COPY_ATTR(xoptions);
-    COPY_ATTR(module_search_path);
+    COPY_ATTR(install_signal_handlers);
+    COPY_OBJ_ATTR(argv);
+    COPY_OBJ_ATTR(executable);
+    COPY_OBJ_ATTR(prefix);
+    COPY_OBJ_ATTR(base_prefix);
+    COPY_OBJ_ATTR(exec_prefix);
+    COPY_OBJ_ATTR(base_exec_prefix);
+    COPY_OBJ_ATTR(warnoptions);
+    COPY_OBJ_ATTR(xoptions);
+    COPY_OBJ_ATTR(module_search_path);
 #undef COPY_ATTR
+#undef COPY_OBJ_ATTR
     return 0;
 }
 
 
+PyObject*
+_PyMainInterpreterConfig_AsDict(const _PyMainInterpreterConfig *config)
+{
+    PyObject *dict, *obj;
+    int res;
+
+    dict = PyDict_New();
+    if (dict == NULL) {
+        return NULL;
+    }
+
+#define SET_ITEM_INT(ATTR) \
+    do { \
+        obj = PyLong_FromLong(config->ATTR); \
+        if (obj == NULL) { \
+            goto fail; \
+        } \
+        res = PyDict_SetItemString(dict, #ATTR, obj); \
+        Py_DECREF(obj); \
+        if (res < 0) { \
+            goto fail; \
+        } \
+    } while (0)
+
+#define SET_ITEM_OBJ(ATTR) \
+    do { \
+        obj = config->ATTR; \
+        if (obj == NULL) { \
+            obj = Py_None; \
+        } \
+        res = PyDict_SetItemString(dict, #ATTR, obj); \
+        if (res < 0) { \
+            goto fail; \
+        } \
+    } while (0)
+
+    SET_ITEM_INT(install_signal_handlers);
+    SET_ITEM_OBJ(argv);
+    SET_ITEM_OBJ(executable);
+    SET_ITEM_OBJ(prefix);
+    SET_ITEM_OBJ(base_prefix);
+    SET_ITEM_OBJ(exec_prefix);
+    SET_ITEM_OBJ(base_exec_prefix);
+    SET_ITEM_OBJ(warnoptions);
+    SET_ITEM_OBJ(xoptions);
+    SET_ITEM_OBJ(module_search_path);
+
+    return dict;
+
+fail:
+    Py_DECREF(dict);
+    return NULL;
+
+#undef SET_ITEM_OBJ
+}
 
 
 _PyInitError
@@ -2530,7 +2780,7 @@ _PyMainInterpreterConfig_Read(_PyMainInterpreterConfig *main_config,
 #define COPY_WSTRLIST(ATTR, LEN, LIST) \
     do { \
         if (ATTR == NULL) { \
-            ATTR = wstrlist_as_pylist(LEN, LIST); \
+            ATTR = _Py_wstrlist_as_pylist(LEN, LIST); \
             if (ATTR == NULL) { \
                 return _Py_INIT_NO_MEMORY(); \
             } \
index 020b199..eac97ad 100755 (executable)
@@ -233,7 +233,7 @@ sed -e 's/[         ]*#.*//' -e '/^[        ]*$/d' |
                        case $doconfig in
                        no)     cc="$cc \$(CCSHARED) \$(PY_CFLAGS) \$(PY_CPPFLAGS)";;
                        *)
-                               cc="$cc \$(PY_STDMODULE_CFLAGS)";;
+                               cc="$cc \$(PY_BUILTIN_MODULE_CFLAGS)";;
                        esac
                        rule="$obj: $src; $cc $cpps -c $src -o $obj"
                        echo "$rule" >>$rulesf
index c66b273..d377f0b 100644 (file)
@@ -361,12 +361,12 @@ MD5Type_copy_impl(MD5object *self)
 /*[clinic input]
 MD5Type.digest
 
-Return the digest value as a string of binary data.
+Return the digest value as a bytes object.
 [clinic start generated code]*/
 
 static PyObject *
 MD5Type_digest_impl(MD5object *self)
-/*[clinic end generated code: output=eb691dc4190a07ec input=7b96e65389412a34]*/
+/*[clinic end generated code: output=eb691dc4190a07ec input=bc0c4397c2994be6]*/
 {
     unsigned char digest[MD5_DIGESTSIZE];
     struct md5_state temp;
index ba42f73..f957e2c 100644 (file)
@@ -653,7 +653,7 @@ mmap_move_method(mmap_object *self, PyObject *args)
 }
 
 static PyObject *
-mmap_closed_get(mmap_object *self)
+mmap_closed_get(mmap_object *self, void *Py_UNUSED(ignored))
 {
 #ifdef MS_WINDOWS
     return PyBool_FromLong(self->map_handle == NULL ? 1 : 0);
index a9028bb..11df679 100644 (file)
@@ -412,6 +412,7 @@ nis_maps (PyObject *self, PyObject *args, PyObject *kwdict)
         PyObject *str = PyUnicode_FromString(maps->map);
         if (!str || PyList_Append(list, str) < 0)
         {
+            Py_XDECREF(str);
             Py_DECREF(list);
             list = NULL;
             break;
index bbbff1e..5403660 100644 (file)
@@ -1422,13 +1422,19 @@ win32_error_object(const char* function, PyObject* filename)
 #endif /* MS_WINDOWS */
 
 static PyObject *
+posix_path_object_error(PyObject *path)
+{
+    return PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path);
+}
+
+static PyObject *
 path_object_error(PyObject *path)
 {
 #ifdef MS_WINDOWS
     return PyErr_SetExcFromWindowsErrWithFilenameObject(
                 PyExc_OSError, 0, path);
 #else
-    return PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path);
+    return posix_path_object_error(path);
 #endif
 }
 
@@ -1450,6 +1456,12 @@ path_error(path_t *path)
 }
 
 static PyObject *
+posix_path_error(path_t *path)
+{
+    return posix_path_object_error(path->object);
+}
+
+static PyObject *
 path_error2(path_t *path, path_t *path2)
 {
     return path_object_error2(path->object, path2->object);
@@ -2434,7 +2446,7 @@ class sched_param_converter(CConverter):
 os.stat
 
     path : path_t(allow_fd=True)
-        Path to be examined; can be string, bytes, path-like object or
+        Path to be examined; can be string, bytes, path-like object or
         open-file-descriptor int.
 
     *
@@ -2462,7 +2474,7 @@ It's an error to use dir_fd or follow_symlinks when specifying path as
 
 static PyObject *
 os_stat_impl(PyObject *module, path_t *path, int dir_fd, int follow_symlinks)
-/*[clinic end generated code: output=7d4976e6f18a59c5 input=270bd64e7bb3c8f7]*/
+/*[clinic end generated code: output=7d4976e6f18a59c5 input=01d362ebcc06996b]*/
 {
     return posix_do_stat("stat", path, dir_fd, follow_symlinks);
 }
@@ -2496,7 +2508,7 @@ os_lstat_impl(PyObject *module, path_t *path, int dir_fd)
 os.access -> bool
 
     path: path_t
-        Path to be tested; can be string or bytes
+        Path to be tested; can be string, bytes, or a path-like object.
 
     mode: int
         Operating-system mode bitfield.  Can be F_OK to test existence,
@@ -2534,7 +2546,7 @@ Note that most operations will use the effective uid/gid, therefore this
 static int
 os_access_impl(PyObject *module, path_t *path, int mode, int dir_fd,
                int effective_ids, int follow_symlinks)
-/*[clinic end generated code: output=cf84158bc90b1a77 input=8e8c3a6ba791fee3]*/
+/*[clinic end generated code: output=cf84158bc90b1a77 input=3ffe4e650ee3bf20]*/
 {
     int return_value;
 
@@ -2726,7 +2738,7 @@ os_fchdir_impl(PyObject *module, int fd)
 os.chmod
 
     path: path_t(allow_fd='PATH_HAVE_FCHMOD')
-        Path to be modified.  May always be specified as a str or bytes.
+        Path to be modified.  May always be specified as a str, bytes, or a path-like object.
         On some platforms, path may also be specified as an open file descriptor.
         If this functionality is unavailable, using it raises an exception.
 
@@ -2757,7 +2769,7 @@ dir_fd and follow_symlinks may not be implemented on your platform.
 static PyObject *
 os_chmod_impl(PyObject *module, path_t *path, int mode, int dir_fd,
               int follow_symlinks)
-/*[clinic end generated code: output=5cf6a94915cc7bff input=7f1618e5e15cc196]*/
+/*[clinic end generated code: output=5cf6a94915cc7bff input=989081551c00293b]*/
 {
     int result;
 
@@ -3077,7 +3089,7 @@ os_fdatasync_impl(PyObject *module, int fd)
 os.chown
 
     path : path_t(allow_fd='PATH_HAVE_FCHOWN')
-        Path to be examined; can be string, bytes, or open-file-descriptor int.
+        Path to be examined; can be string, bytes, a path-like object, or open-file-descriptor int.
 
     uid: uid_t
 
@@ -3115,7 +3127,7 @@ dir_fd and follow_symlinks may not be implemented on your platform.
 static PyObject *
 os_chown_impl(PyObject *module, path_t *path, uid_t uid, gid_t gid,
               int dir_fd, int follow_symlinks)
-/*[clinic end generated code: output=4beadab0db5f70cd input=a61cc35574814d5d]*/
+/*[clinic end generated code: output=4beadab0db5f70cd input=b08c5ec67996a97d]*/
 {
     int result;
 
@@ -3642,7 +3654,7 @@ os.listdir
 
 Return a list containing the names of the files in the directory.
 
-path can be specified as either str or bytes.  If path is bytes,
+path can be specified as either str, bytes, or a path-like object.  If path is bytes,
   the filenames returned will also be bytes; in all other circumstances
   the filenames returned will be str.
 If path is None, uses the path='.'.
@@ -3658,7 +3670,7 @@ entries '.' and '..' even if they are present in the directory.
 
 static PyObject *
 os_listdir_impl(PyObject *module, path_t *path)
-/*[clinic end generated code: output=293045673fcd1a75 input=09e300416e3cd729]*/
+/*[clinic end generated code: output=293045673fcd1a75 input=e3f58030f538295d]*/
 {
 #if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR)
     return _listdir_windows_no_opendir(path, NULL);
@@ -4633,7 +4645,6 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns,
     int result;
 #endif
 
-    PyObject *return_value = NULL;
     utime_t utime;
 
     memset(&utime, 0, sizeof(utime_t));
@@ -4642,7 +4653,7 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns,
         PyErr_SetString(PyExc_ValueError,
                      "utime: you may specify either 'times'"
                      " or 'ns' but not both");
-        goto exit;
+        return NULL;
     }
 
     if (times && (times != Py_None)) {
@@ -4652,14 +4663,14 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns,
             PyErr_SetString(PyExc_TypeError,
                          "utime: 'times' must be either"
                          " a tuple of two ints or None");
-            goto exit;
+            return NULL;
         }
         utime.now = 0;
         if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 0),
                                      &a_sec, &a_nsec, _PyTime_ROUND_FLOOR) == -1 ||
             _PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 1),
                                      &m_sec, &m_nsec, _PyTime_ROUND_FLOOR) == -1) {
-            goto exit;
+            return NULL;
         }
         utime.atime_s = a_sec;
         utime.atime_ns = a_nsec;
@@ -4670,14 +4681,14 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns,
         if (!PyTuple_CheckExact(ns) || (PyTuple_Size(ns) != 2)) {
             PyErr_SetString(PyExc_TypeError,
                          "utime: 'ns' must be a tuple of two ints");
-            goto exit;
+            return NULL;
         }
         utime.now = 0;
         if (!split_py_long_to_s_and_ns(PyTuple_GET_ITEM(ns, 0),
                                       &utime.atime_s, &utime.atime_ns) ||
             !split_py_long_to_s_and_ns(PyTuple_GET_ITEM(ns, 1),
                                        &utime.mtime_s, &utime.mtime_ns)) {
-            goto exit;
+            return NULL;
         }
     }
     else {
@@ -4687,20 +4698,20 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns,
 
 #if !defined(UTIME_HAVE_NOFOLLOW_SYMLINKS)
     if (follow_symlinks_specified("utime", follow_symlinks))
-        goto exit;
+        return NULL;
 #endif
 
     if (path_and_dir_fd_invalid("utime", path, dir_fd) ||
         dir_fd_and_fd_invalid("utime", dir_fd, path->fd) ||
         fd_and_follow_symlinks_invalid("utime", path->fd, follow_symlinks))
-        goto exit;
+        return NULL;
 
 #if !defined(HAVE_UTIMENSAT)
     if ((dir_fd != DEFAULT_DIR_FD) && (!follow_symlinks)) {
         PyErr_SetString(PyExc_ValueError,
                      "utime: cannot use dir_fd and follow_symlinks "
                      "together on this platform");
-        goto exit;
+        return NULL;
     }
 #endif
 
@@ -4712,7 +4723,7 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns,
     Py_END_ALLOW_THREADS
     if (hFile == INVALID_HANDLE_VALUE) {
         path_error(path);
-        goto exit;
+        return NULL;
     }
 
     if (utime.now) {
@@ -4729,8 +4740,10 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns,
            something is wrong with the file, when it also
            could be the time stamp that gives a problem. */
         PyErr_SetFromWindowsErr(0);
-        goto exit;
+        CloseHandle(hFile);
+        return NULL;
     }
+    CloseHandle(hFile);
 #else /* MS_WINDOWS */
     Py_BEGIN_ALLOW_THREADS
 
@@ -4758,21 +4771,13 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns,
 
     if (result < 0) {
         /* see previous comment about not putting filename in error here */
-        return_value = posix_error();
-        goto exit;
+        posix_error();
+        return NULL;
     }
 
 #endif /* MS_WINDOWS */
 
-    Py_INCREF(Py_None);
-    return_value = Py_None;
-
-exit:
-#ifdef MS_WINDOWS
-    if (hFile != INVALID_HANDLE_VALUE)
-        CloseHandle(hFile);
-#endif
-    return return_value;
+    Py_RETURN_NONE;
 }
 
 /* Process operations */
@@ -5097,7 +5102,7 @@ os_execve_impl(PyObject *module, path_t *path, PyObject *argv, PyObject *env)
 
     /* If we get here it's definitely an error */
 
-    path_error(path);
+    posix_path_error(path);
 
     free_string_array(envlist, envc);
   fail:
@@ -6224,8 +6229,7 @@ os_getgroups_impl(PyObject *module)
     } else {
         alt_grouplist = PyMem_New(gid_t, n);
         if (alt_grouplist == NULL) {
-            errno = EINVAL;
-            return posix_error();
+            return PyErr_NoMemory();
         }
     }
 
@@ -6250,8 +6254,7 @@ os_getgroups_impl(PyObject *module)
             } else {
                 alt_grouplist = PyMem_New(gid_t, n);
                 if (alt_grouplist == NULL) {
-                    errno = EINVAL;
-                    return posix_error();
+                    return PyErr_NoMemory();
                 }
                 n = getgroups(n, alt_grouplist);
                 if (n == -1) {
@@ -8009,11 +8012,7 @@ os_read_impl(PyObject *module, int fd, Py_ssize_t length)
         return posix_error();
     }
 
-#ifdef MS_WINDOWS
-    /* On Windows, the count parameter of read() is an int */
-    if (length > INT_MAX)
-        length = INT_MAX;
-#endif
+    length = Py_MIN(length, _PY_READ_MAX);
 
     buffer = PyBytes_FromStringAndSize((char *)NULL, length);
     if (buffer == NULL)
@@ -9060,7 +9059,7 @@ os_truncate_impl(PyObject *module, path_t *path, Py_off_t length)
     _Py_END_SUPPRESS_IPH
     Py_END_ALLOW_THREADS
     if (result < 0)
-        return path_error(path);
+        return posix_path_error(path);
 
     Py_RETURN_NONE;
 }
@@ -10961,7 +10960,7 @@ os.getxattr
 
 Return the value of extended attribute attribute on path.
 
-path may be either a string or an open file descriptor.
+path may be either a string, a path-like object, or an open file descriptor.
 If follow_symlinks is False, and the last element of the path is a symbolic
   link, getxattr will examine the symbolic link itself instead of the file
   the link points to.
@@ -10971,7 +10970,7 @@ If follow_symlinks is False, and the last element of the path is a symbolic
 static PyObject *
 os_getxattr_impl(PyObject *module, path_t *path, path_t *attribute,
                  int follow_symlinks)
-/*[clinic end generated code: output=5f2f44200a43cff2 input=8c8ea3bab78d89c2]*/
+/*[clinic end generated code: output=5f2f44200a43cff2 input=025789491708f7eb]*/
 {
     Py_ssize_t i;
     PyObject *buffer = NULL;
@@ -11033,7 +11032,7 @@ os.setxattr
 
 Set extended attribute attribute on path to value.
 
-path may be either a string or an open file descriptor.
+path may be either a string, a path-like object,  or an open file descriptor.
 If follow_symlinks is False, and the last element of the path is a symbolic
   link, setxattr will modify the symbolic link itself instead of the file
   the link points to.
@@ -11043,7 +11042,7 @@ If follow_symlinks is False, and the last element of the path is a symbolic
 static PyObject *
 os_setxattr_impl(PyObject *module, path_t *path, path_t *attribute,
                  Py_buffer *value, int flags, int follow_symlinks)
-/*[clinic end generated code: output=98b83f63fdde26bb input=f0d26833992015c2]*/
+/*[clinic end generated code: output=98b83f63fdde26bb input=c17c0103009042f0]*/
 {
     ssize_t result;
 
@@ -11081,7 +11080,7 @@ os.removexattr
 
 Remove extended attribute attribute on path.
 
-path may be either a string or an open file descriptor.
+path may be either a string, a path-like object, or an open file descriptor.
 If follow_symlinks is False, and the last element of the path is a symbolic
   link, removexattr will modify the symbolic link itself instead of the file
   the link points to.
@@ -11091,7 +11090,7 @@ If follow_symlinks is False, and the last element of the path is a symbolic
 static PyObject *
 os_removexattr_impl(PyObject *module, path_t *path, path_t *attribute,
                     int follow_symlinks)
-/*[clinic end generated code: output=521a51817980cda6 input=cdb54834161e3329]*/
+/*[clinic end generated code: output=521a51817980cda6 input=3d9a7d36fe2f7c4e]*/
 {
     ssize_t result;
 
@@ -11124,7 +11123,7 @@ os.listxattr
 
 Return a list of extended attributes on path.
 
-path may be either None, a string, or an open file descriptor.
+path may be either None, a string, a path-like object, or an open file descriptor.
 if path is None, listxattr will examine the current directory.
 If follow_symlinks is False, and the last element of the path is a symbolic
   link, listxattr will examine the symbolic link itself instead of the file
@@ -11133,7 +11132,7 @@ If follow_symlinks is False, and the last element of the path is a symbolic
 
 static PyObject *
 os_listxattr_impl(PyObject *module, path_t *path, int follow_symlinks)
-/*[clinic end generated code: output=bebdb4e2ad0ce435 input=08cca53ac0b07c13]*/
+/*[clinic end generated code: output=bebdb4e2ad0ce435 input=9826edf9fdb90869]*/
 {
     Py_ssize_t i;
     PyObject *result = NULL;
@@ -12380,7 +12379,7 @@ os.scandir
 
 Return an iterator of DirEntry objects for given path.
 
-path can be specified as either str, bytes or path-like object.  If path
+path can be specified as either str, bytes, or a path-like object.  If path
 is bytes, the names of yielded DirEntry objects will also be bytes; in
 all other circumstances they will be str.
 
@@ -12389,7 +12388,7 @@ If path is None, uses the path='.'.
 
 static PyObject *
 os_scandir_impl(PyObject *module, path_t *path)
-/*[clinic end generated code: output=6eb2668b675ca89e input=b139dc1c57f60846]*/
+/*[clinic end generated code: output=6eb2668b675ca89e input=6bdd312708fc3bb0]*/
 {
     ScandirIterator *iterator;
 #ifdef MS_WINDOWS
index 21c2b54..810427a 100644 (file)
@@ -163,7 +163,7 @@ pwd_getpwnam_impl(PyObject *module, PyObject *arg)
         goto out;
     if ((p = getpwnam(name)) == NULL) {
         PyErr_Format(PyExc_KeyError,
-                     "getpwnam(): name not found: %S", arg);
+                     "getpwnam(): name not found: %R", arg);
         goto out;
     }
     retval = mkpwent(p);
index c52079e..ab3dac6 100644 (file)
@@ -243,8 +243,10 @@ string_intern(xmlparseobject *self, const char* str)
     if (!value) {
         if (PyDict_SetItem(self->intern, result, result) == 0)
             return result;
-        else
+        else {
+            Py_DECREF(result);
             return NULL;
+        }
     }
     Py_INCREF(value);
     Py_DECREF(result);
@@ -393,6 +395,7 @@ my_StartElementHandler(void *userData,
                 flag_error(self);
                 Py_DECREF(n);
                 Py_DECREF(v);
+                Py_DECREF(container);
                 return;
             }
             else {
@@ -401,12 +404,14 @@ my_StartElementHandler(void *userData,
             }
         }
         args = string_intern(self, name);
-        if (args != NULL)
-            args = Py_BuildValue("(NN)", args, container);
         if (args == NULL) {
             Py_DECREF(container);
             return;
         }
+        args = Py_BuildValue("(NN)", args, container);
+        if (args == NULL) {
+            return;
+        }
         /* Container is now a borrowed reference; ignore it. */
         self->in_callback = 1;
         rv = call_with_frame("StartElement", __LINE__,
@@ -565,7 +570,6 @@ my_ElementDeclHandler(void *userData,
         }
         args = Py_BuildValue("NN", nameobj, modelobj);
         if (args == NULL) {
-            Py_DECREF(modelobj);
             flag_error(self);
             goto finally;
         }
index 7756e6b..fa7e7d1 100644 (file)
@@ -936,8 +936,7 @@ on_completion_display_matches_hook(char **matches,
         s = decode(matches[i+1]);
         if (s == NULL)
             goto error;
-        if (PyList_SetItem(m, i, s) == -1)
-            goto error;
+        PyList_SET_ITEM(m, i, s);
     }
     sub = decode(matches[0]);
     r = PyObject_CallFunction(readlinestate_global->completion_display_matches_hook,
index d6b8fc1..93d896a 100644 (file)
@@ -279,6 +279,10 @@ select_select(PyObject *self, PyObject *args)
         if (tvp) {
             timeout = deadline - _PyTime_GetMonotonicClock();
             if (timeout < 0) {
+                /* bpo-35310: lists were unmodified -- clear them explicitly */
+                FD_ZERO(&ifdset);
+                FD_ZERO(&ofdset);
+                FD_ZERO(&efdset);
                 n = 0;
                 break;
             }
@@ -648,10 +652,7 @@ poll_poll(pollObject *self, PyObject *args)
             goto error;
         }
         PyTuple_SET_ITEM(value, 1, num);
-        if ((PyList_SetItem(result_list, j, value)) == -1) {
-            Py_DECREF(value);
-            goto error;
-        }
+        PyList_SET_ITEM(result_list, j, value);
         i++;
     }
     return result_list;
@@ -977,10 +978,7 @@ devpoll_poll(devpollObject *self, PyObject *args)
         Py_DECREF(num2);
         if (value == NULL)
             goto error;
-        if ((PyList_SetItem(result_list, i, value)) == -1) {
-            Py_DECREF(value);
-            goto error;
-        }
+        PyList_SET_ITEM(result_list, i, value);
     }
 
     return result_list;
@@ -1023,7 +1021,7 @@ Close the devpoll file descriptor. Further operations on the devpoll\n\
 object will raise an exception.");
 
 static PyObject*
-devpoll_get_closed(devpollObject *self)
+devpoll_get_closed(devpollObject *self, void *Py_UNUSED(ignored))
 {
     if (self->fd_devpoll < 0)
         Py_RETURN_TRUE;
@@ -1346,7 +1344,7 @@ Close the epoll control file descriptor. Further operations on the epoll\n\
 object will raise an exception.");
 
 static PyObject*
-pyepoll_get_closed(pyEpoll_Object *self)
+pyepoll_get_closed(pyEpoll_Object *self, void *Py_UNUSED(ignored))
 {
     if (self->epfd < 0)
         Py_RETURN_TRUE;
@@ -2072,7 +2070,7 @@ Close the kqueue control file descriptor. Further operations on the kqueue\n\
 object will raise an exception.");
 
 static PyObject*
-kqueue_queue_get_closed(kqueue_queue_Object *self)
+kqueue_queue_get_closed(kqueue_queue_Object *self, void *Py_UNUSED(ignored))
 {
     if (self->kqfd < 0)
         Py_RETURN_TRUE;
index 358cd1e..998ebd4 100644 (file)
@@ -338,12 +338,12 @@ SHA1Type_copy_impl(SHA1object *self)
 /*[clinic input]
 SHA1Type.digest
 
-Return the digest value as a string of binary data.
+Return the digest value as a bytes object.
 [clinic start generated code]*/
 
 static PyObject *
 SHA1Type_digest_impl(SHA1object *self)
-/*[clinic end generated code: output=2f05302a7aa2b5cb input=205d47e1927fd009]*/
+/*[clinic end generated code: output=2f05302a7aa2b5cb input=13824b35407444bd]*/
 {
     unsigned char digest[SHA1_DIGESTSIZE];
     struct sha1_state temp;
index 3599eaf..20b5f02 100644 (file)
@@ -428,12 +428,12 @@ SHA256Type_copy_impl(SHAobject *self)
 /*[clinic input]
 SHA256Type.digest
 
-Return the digest value as a string of binary data.
+Return the digest value as a bytes object.
 [clinic start generated code]*/
 
 static PyObject *
 SHA256Type_digest_impl(SHAobject *self)
-/*[clinic end generated code: output=46616a5e909fbc3d input=1fb752e58954157d]*/
+/*[clinic end generated code: output=46616a5e909fbc3d input=f1f4cfea5cbde35c]*/
 {
     unsigned char digest[SHA_DIGESTSIZE];
     SHAobject temp;
index d8c846a..e070e43 100644 (file)
@@ -493,12 +493,12 @@ SHA512Type_copy_impl(SHAobject *self)
 /*[clinic input]
 SHA512Type.digest
 
-Return the digest value as a string of binary data.
+Return the digest value as a bytes object.
 [clinic start generated code]*/
 
 static PyObject *
 SHA512Type_digest_impl(SHAobject *self)
-/*[clinic end generated code: output=1080bbeeef7dde1b input=60c2cede9e023018]*/
+/*[clinic end generated code: output=1080bbeeef7dde1b input=f6470dd359071f4b]*/
 {
     unsigned char digest[SHA_DIGESTSIZE];
     SHAobject temp;
index eeade2e..e6d3f8b 100644 (file)
@@ -360,10 +360,14 @@ remove_unusable_flags(PyObject *m)
         else {
             if (PyDict_GetItemString(
                     dict,
-                    win_runtime_flags[i].flag_name) != NULL) {
-                PyDict_DelItemString(
-                    dict,
-                    win_runtime_flags[i].flag_name);
+                    win_runtime_flags[i].flag_name) != NULL)
+            {
+                if (PyDict_DelItemString(
+                        dict,
+                        win_runtime_flags[i].flag_name))
+                {
+                    PyErr_Clear();
+                }
             }
         }
     }
@@ -2157,14 +2161,18 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
 
         if (!PyArg_ParseTuple(args, "ss|HH:getsockaddrarg",
                                 &type, &name, &sa->salg_feat, &sa->salg_mask))
+        {
             return 0;
-        /* sockaddr_alg has fixed-sized char arrays for type and name */
-        if (strlen(type) > sizeof(sa->salg_type)) {
+        }
+        /* sockaddr_alg has fixed-sized char arrays for type, and name
+         * both must be NULL terminated.
+         */
+        if (strlen(type) >= sizeof(sa->salg_type)) {
             PyErr_SetString(PyExc_ValueError, "AF_ALG type too long.");
             return 0;
         }
         strncpy((char *)sa->salg_type, type, sizeof(sa->salg_type));
-        if (strlen(name) > sizeof(sa->salg_name)) {
+        if (strlen(name) >= sizeof(sa->salg_name)) {
             PyErr_SetString(PyExc_ValueError, "AF_ALG name too long.");
             return 0;
         }
@@ -6269,9 +6277,11 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs)
         if (single == NULL)
             goto err;
 
-        if (PyList_Append(all, single))
+        if (PyList_Append(all, single)) {
+            Py_DECREF(single);
             goto err;
-        Py_XDECREF(single);
+        }
+        Py_DECREF(single);
     }
     Py_XDECREF(idna);
     if (res0)
index b4aa77f..7601b68 100644 (file)
@@ -119,11 +119,11 @@ termios_tcgetattr(PyObject *self, PyObject *args)
     PyList_SetItem(v, 3, PyLong_FromLong((long)mode.c_lflag));
     PyList_SetItem(v, 4, PyLong_FromLong((long)ispeed));
     PyList_SetItem(v, 5, PyLong_FromLong((long)ospeed));
-    PyList_SetItem(v, 6, cc);
-    if (PyErr_Occurred()){
+    if (PyErr_Occurred()) {
         Py_DECREF(v);
         goto err;
     }
+    PyList_SetItem(v, 6, cc);
     return v;
   err:
     Py_DECREF(cc);
index 7264ad6..13a174a 100644 (file)
@@ -994,7 +994,7 @@ of the timezone or altzone attributes on the time module.");
 #endif /* HAVE_MKTIME */
 
 #ifdef HAVE_WORKING_TZSET
-static void PyInit_timezone(PyObject *module);
+static int init_timezone(PyObject *module);
 
 static PyObject *
 time_tzset(PyObject *self, PyObject *unused)
@@ -1009,7 +1009,9 @@ time_tzset(PyObject *self, PyObject *unused)
     tzset();
 
     /* Reset timezone, altzone, daylight and tzname */
-    PyInit_timezone(m);
+    if (init_timezone(m) < 0) {
+         return NULL;
+    }
     Py_DECREF(m);
     if (PyErr_Occurred())
         return NULL;
@@ -1510,7 +1512,7 @@ get_zone(char *zone, int n, struct tm *p)
 #endif
 }
 
-static int
+static time_t
 get_gmtoff(time_t t, struct tm *p)
 {
 #ifdef HAVE_STRUCT_TM_TM_ZONE
@@ -1521,8 +1523,11 @@ get_gmtoff(time_t t, struct tm *p)
 }
 #endif /* !defined(HAVE_TZNAME) || defined(__GLIBC__) || defined(__CYGWIN__) */
 
-static void
-PyInit_timezone(PyObject *m) {
+static int
+init_timezone(PyObject *m)
+{
+    assert(!PyErr_Occurred());
+
     /* This code moved from PyInit_time wholesale to allow calling it from
     time_tzset. In the future, some parts of it can be moved back
     (for platforms that don't HAVE_WORKING_TZSET, when we know what they
@@ -1550,26 +1555,49 @@ PyInit_timezone(PyObject *m) {
 #endif
     PyModule_AddIntConstant(m, "daylight", daylight);
     otz0 = PyUnicode_DecodeLocale(tzname[0], "surrogateescape");
+    if (otz0 == NULL) {
+        return -1;
+    }
     otz1 = PyUnicode_DecodeLocale(tzname[1], "surrogateescape");
-    PyModule_AddObject(m, "tzname", Py_BuildValue("(NN)", otz0, otz1));
+    if (otz1 == NULL) {
+        Py_DECREF(otz0);
+        return -1;
+    }
+    PyObject *tzname_obj = Py_BuildValue("(NN)", otz0, otz1);
+    if (tzname_obj == NULL) {
+        return -1;
+    }
+    PyModule_AddObject(m, "tzname", tzname_obj);
 #else /* !HAVE_TZNAME || __GLIBC__ || __CYGWIN__*/
     {
 #define YEAR ((time_t)((365 * 24 + 6) * 3600))
         time_t t;
         struct tm p;
-        long janzone, julyzone;
+        time_t janzone_t, julyzone_t;
         char janname[10], julyname[10];
         t = (time((time_t *)0) / YEAR) * YEAR;
         _PyTime_localtime(t, &p);
         get_zone(janname, 9, &p);
-        janzone = -get_gmtoff(t, &p);
+        janzone_t = -get_gmtoff(t, &p);
         janname[9] = '\0';
         t += YEAR/2;
         _PyTime_localtime(t, &p);
         get_zone(julyname, 9, &p);
-        julyzone = -get_gmtoff(t, &p);
+        julyzone_t = -get_gmtoff(t, &p);
         julyname[9] = '\0';
 
+        /* Sanity check, don't check for the validity of timezones.
+           In practice, it should be more in range -12 hours .. +14 hours. */
+#define MAX_TIMEZONE (48 * 3600)
+        if (janzone_t < -MAX_TIMEZONE || janzone_t > MAX_TIMEZONE
+            || julyzone_t < -MAX_TIMEZONE || julyzone_t > MAX_TIMEZONE)
+        {
+            PyErr_SetString(PyExc_RuntimeError, "invalid GMT offset");
+            return -1;
+        }
+        int janzone = (int)janzone_t;
+        int julyzone = (int)julyzone_t;
+
         if( janzone < julyzone ) {
             /* DST is reversed in the southern hemisphere */
             PyModule_AddIntConstant(m, "timezone", julyzone);
@@ -1598,6 +1626,11 @@ PyInit_timezone(PyObject *m) {
                        Py_BuildValue("(zz)", _tzname[0], _tzname[1]));
 #endif /* __CYGWIN__ */
 #endif /* !HAVE_TZNAME || __GLIBC__ || __CYGWIN__*/
+
+    if (PyErr_Occurred()) {
+        return -1;
+    }
+    return 0;
 }
 
 
@@ -1698,7 +1731,11 @@ PyInit_time(void)
         return NULL;
 
     /* Set, or reset, module variables like time.timezone */
-    PyInit_timezone(m);
+    if (init_timezone(m) < 0) {
+        return NULL;
+    }
+
+#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_CLOCK_SETTIME) || defined(HAVE_CLOCK_GETRES)
 
 #ifdef CLOCK_REALTIME
     PyModule_AddIntMacro(m, CLOCK_REALTIME);
@@ -1728,6 +1765,8 @@ PyInit_time(void)
     PyModule_AddIntMacro(m, CLOCK_UPTIME);
 #endif
 
+#endif  /* defined(HAVE_CLOCK_GETTIME) || defined(HAVE_CLOCK_SETTIME) || defined(HAVE_CLOCK_GETRES) */
+
     if (!initialized) {
         if (PyStructSequence_InitType2(&StructTimeType,
                                        &struct_time_type_desc) < 0)
@@ -1737,6 +1776,10 @@ PyInit_time(void)
     PyModule_AddIntConstant(m, "_STRUCT_TM_ITEMS", 11);
     PyModule_AddObject(m, "struct_time", (PyObject*) &StructTimeType);
     initialized = 1;
+
+    if (PyErr_Occurred()) {
+        return NULL;
+    }
     return m;
 }
 
index 11242d7..d2593b1 100644 (file)
@@ -89,7 +89,7 @@ spamlist_init(spamlistobject *self, PyObject *args, PyObject *kwds)
 }
 
 static PyObject *
-spamlist_state_get(spamlistobject *self)
+spamlist_state_get(spamlistobject *self, void *Py_UNUSED(ignored))
 {
     return PyLong_FromLong(self->state);
 }
index cd587b4..21b5dc7 100644 (file)
@@ -117,11 +117,11 @@ newcompobject(PyTypeObject *type)
 static void*
 PyZlib_Malloc(voidpf ctx, uInt items, uInt size)
 {
-    if (items > (size_t)PY_SSIZE_T_MAX / size)
+    if (size != 0 && items > (size_t)PY_SSIZE_T_MAX / size)
         return NULL;
     /* PyMem_Malloc() cannot be used: the GIL is not held when
        inflate() and deflate() are called */
-    return PyMem_RawMalloc(items * size);
+    return PyMem_RawMalloc((size_t)items * (size_t)size);
 }
 
 static void
index 692b7be..3f3e6bc 100644 (file)
@@ -41,7 +41,6 @@ _getbytevalue(PyObject* arg, int *value)
     } else {
         PyObject *index = PyNumber_Index(arg);
         if (index == NULL) {
-            PyErr_Format(PyExc_TypeError, "an integer is required");
             *value = -1;
             return 0;
         }
@@ -821,7 +820,7 @@ bytearray_init(PyByteArrayObject *self, PyObject *args, PyObject *kwds)
     if (PyIndex_Check(arg)) {
         count = PyNumber_AsSsize_t(arg, PyExc_OverflowError);
         if (count == -1 && PyErr_Occurred()) {
-            if (PyErr_ExceptionMatches(PyExc_OverflowError))
+            if (!PyErr_ExceptionMatches(PyExc_TypeError))
                 return -1;
             PyErr_Clear();  /* fall through */
         }
index 32ff5af..711faba 100644 (file)
@@ -2597,7 +2597,7 @@ bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
     if (PyIndex_Check(x)) {
         size = PyNumber_AsSsize_t(x, PyExc_OverflowError);
         if (size == -1 && PyErr_Occurred()) {
-            if (PyErr_ExceptionMatches(PyExc_OverflowError))
+            if (!PyErr_ExceptionMatches(PyExc_TypeError))
                 return NULL;
             PyErr_Clear();  /* fall through */
         }
@@ -2640,49 +2640,83 @@ fail:
     return NULL;
 }
 
-#define _PyBytes_FROM_LIST_BODY(x, GET_ITEM)                                \
-    do {                                                                    \
-        PyObject *bytes;                                                    \
-        Py_ssize_t i;                                                       \
-        Py_ssize_t value;                                                   \
-        char *str;                                                          \
-        PyObject *item;                                                     \
-                                                                            \
-        bytes = PyBytes_FromStringAndSize(NULL, Py_SIZE(x));                \
-        if (bytes == NULL)                                                  \
-            return NULL;                                                    \
-        str = ((PyBytesObject *)bytes)->ob_sval;                            \
-                                                                            \
-        for (i = 0; i < Py_SIZE(x); i++) {                                  \
-            item = GET_ITEM((x), i);                                        \
-            value = PyNumber_AsSsize_t(item, NULL);                         \
-            if (value == -1 && PyErr_Occurred())                            \
-                goto error;                                                 \
-                                                                            \
-            if (value < 0 || value >= 256) {                                \
-                PyErr_SetString(PyExc_ValueError,                           \
-                                "bytes must be in range(0, 256)");          \
-                goto error;                                                 \
-            }                                                               \
-            *str++ = (char) value;                                          \
-        }                                                                   \
-        return bytes;                                                       \
-                                                                            \
-    error:                                                                  \
-        Py_DECREF(bytes);                                                   \
-        return NULL;                                                        \
-    } while (0)
-
 static PyObject*
 _PyBytes_FromList(PyObject *x)
 {
-    _PyBytes_FROM_LIST_BODY(x, PyList_GET_ITEM);
+    Py_ssize_t i, size = PyList_GET_SIZE(x);
+    Py_ssize_t value;
+    char *str;
+    PyObject *item;
+    _PyBytesWriter writer;
+
+    _PyBytesWriter_Init(&writer);
+    str = _PyBytesWriter_Alloc(&writer, size);
+    if (str == NULL)
+        return NULL;
+    writer.overallocate = 1;
+    size = writer.allocated;
+
+    for (i = 0; i < PyList_GET_SIZE(x); i++) {
+        item = PyList_GET_ITEM(x, i);
+        Py_INCREF(item);
+        value = PyNumber_AsSsize_t(item, NULL);
+        Py_DECREF(item);
+        if (value == -1 && PyErr_Occurred())
+            goto error;
+
+        if (value < 0 || value >= 256) {
+            PyErr_SetString(PyExc_ValueError,
+                            "bytes must be in range(0, 256)");
+            goto error;
+        }
+
+        if (i >= size) {
+            str = _PyBytesWriter_Resize(&writer, str, size+1);
+            if (str == NULL)
+                return NULL;
+            size = writer.allocated;
+        }
+        *str++ = (char) value;
+    }
+    return _PyBytesWriter_Finish(&writer, str);
+
+  error:
+    _PyBytesWriter_Dealloc(&writer);
+    return NULL;
 }
 
 static PyObject*
 _PyBytes_FromTuple(PyObject *x)
 {
-    _PyBytes_FROM_LIST_BODY(x, PyTuple_GET_ITEM);
+    PyObject *bytes;
+    Py_ssize_t i, size = PyTuple_GET_SIZE(x);
+    Py_ssize_t value;
+    char *str;
+    PyObject *item;
+
+    bytes = PyBytes_FromStringAndSize(NULL, size);
+    if (bytes == NULL)
+        return NULL;
+    str = ((PyBytesObject *)bytes)->ob_sval;
+
+    for (i = 0; i < size; i++) {
+        item = PyTuple_GET_ITEM(x, i);
+        value = PyNumber_AsSsize_t(item, NULL);
+        if (value == -1 && PyErr_Occurred())
+            goto error;
+
+        if (value < 0 || value >= 256) {
+            PyErr_SetString(PyExc_ValueError,
+                            "bytes must be in range(0, 256)");
+            goto error;
+        }
+        *str++ = (char) value;
+    }
+    return bytes;
+
+  error:
+    Py_DECREF(bytes);
+    return NULL;
 }
 
 static PyObject *
@@ -2779,6 +2813,9 @@ PyBytes_FromObject(PyObject *x)
             Py_DECREF(it);
             return result;
         }
+        if (!PyErr_ExceptionMatches(PyExc_TypeError)) {
+            return NULL;
+        }
     }
 
     PyErr_Format(PyExc_TypeError,
index acd3de6..4e15b44 100644 (file)
@@ -201,7 +201,7 @@ PyCapsule_Import(const char *name, int no_block)
     char *name_dup = (char *)PyMem_MALLOC(name_length);
 
     if (!name_dup) {
-        return NULL;
+        return PyErr_NoMemory();
     }
 
     memcpy(name_dup, name, name_length);
index 7b05e61..6f8915a 100644 (file)
@@ -111,7 +111,7 @@ cell_get_contents(PyCellObject *op, void *closure)
 }
 
 static int
-cell_set_contents(PyCellObject *op, PyObject *obj)
+cell_set_contents(PyCellObject *op, PyObject *obj, void *Py_UNUSED(ignored))
 {
     Py_XINCREF(obj);
     Py_XSETREF(op->ob_ref, obj);
index 3dc23b7..f45d6ae 100644 (file)
@@ -77,8 +77,6 @@ method_reduce(PyMethodObject *im)
 {
     PyObject *self = PyMethod_GET_SELF(im);
     PyObject *func = PyMethod_GET_FUNCTION(im);
-    PyObject *builtins;
-    PyObject *getattr;
     PyObject *funcname;
     _Py_IDENTIFIER(getattr);
 
@@ -86,9 +84,8 @@ method_reduce(PyMethodObject *im)
     if (funcname == NULL) {
         return NULL;
     }
-    builtins = PyEval_GetBuiltins();
-    getattr = _PyDict_GetItemId(builtins, &PyId_getattr);
-    return Py_BuildValue("O(ON)", getattr, self, funcname);
+    return Py_BuildValue("N(ON)", _PyEval_GetBuiltinId(&PyId_getattr),
+                         self, funcname);
 }
 
 static PyMethodDef method_methods[] = {
index 6014039..277fed9 100644 (file)
@@ -344,7 +344,7 @@ wrapperdescr_raw_call(PyWrapperDescrObject *descr, PyObject *self,
     wrapperfunc wrapper = descr->d_base->wrapper;
 
     if (descr->d_base->flags & PyWrapperFlag_KEYWORDS) {
-        wrapperfunc_kwds wk = (wrapperfunc_kwds)wrapper;
+        wrapperfunc_kwds wk = (wrapperfunc_kwds)(void(*)(void))wrapper;
         return (*wk)(self, args, descr->d_wrapped, kwds);
     }
 
@@ -439,7 +439,7 @@ calculate_qualname(PyDescrObject *descr)
 }
 
 static PyObject *
-descr_get_qualname(PyDescrObject *descr)
+descr_get_qualname(PyDescrObject *descr, void *Py_UNUSED(ignored))
 {
     if (descr->d_qualname == NULL)
         descr->d_qualname = calculate_qualname(descr);
@@ -450,14 +450,9 @@ descr_get_qualname(PyDescrObject *descr)
 static PyObject *
 descr_reduce(PyDescrObject *descr)
 {
-    PyObject *builtins;
-    PyObject *getattr;
     _Py_IDENTIFIER(getattr);
-
-    builtins = PyEval_GetBuiltins();
-    getattr = _PyDict_GetItemId(builtins, &PyId_getattr);
-    return Py_BuildValue("O(OO)", getattr, PyDescr_TYPE(descr),
-                         PyDescr_NAME(descr));
+    return Py_BuildValue("N(OO)", _PyEval_GetBuiltinId(&PyId_getattr),
+                         PyDescr_TYPE(descr), PyDescr_NAME(descr));
 }
 
 static PyMethodDef descr_methods[] = {
@@ -1088,13 +1083,9 @@ wrapper_repr(wrapperobject *wp)
 static PyObject *
 wrapper_reduce(wrapperobject *wp)
 {
-    PyObject *builtins;
-    PyObject *getattr;
     _Py_IDENTIFIER(getattr);
-
-    builtins = PyEval_GetBuiltins();
-    getattr = _PyDict_GetItemId(builtins, &PyId_getattr);
-    return Py_BuildValue("O(OO)", getattr, wp->self, PyDescr_NAME(wp->descr));
+    return Py_BuildValue("N(OO)", _PyEval_GetBuiltinId(&PyId_getattr),
+                         wp->self, PyDescr_NAME(wp->descr));
 }
 
 static PyMethodDef wrapper_methods[] = {
@@ -1108,7 +1099,7 @@ static PyMemberDef wrapper_members[] = {
 };
 
 static PyObject *
-wrapper_objclass(wrapperobject *wp)
+wrapper_objclass(wrapperobject *wp, void *Py_UNUSED(ignored))
 {
     PyObject *c = (PyObject *)PyDescr_TYPE(wp->descr);
 
@@ -1117,7 +1108,7 @@ wrapper_objclass(wrapperobject *wp)
 }
 
 static PyObject *
-wrapper_name(wrapperobject *wp)
+wrapper_name(wrapperobject *wp, void *Py_UNUSED(ignored))
 {
     const char *s = wp->descr->d_base->name;
 
@@ -1125,21 +1116,21 @@ wrapper_name(wrapperobject *wp)
 }
 
 static PyObject *
-wrapper_doc(wrapperobject *wp, void *closure)
+wrapper_doc(wrapperobject *wp, void *Py_UNUSED(ignored))
 {
     return _PyType_GetDocFromInternalDoc(wp->descr->d_base->name, wp->descr->d_base->doc);
 }
 
 static PyObject *
-wrapper_text_signature(wrapperobject *wp, void *closure)
+wrapper_text_signature(wrapperobject *wp, void *Py_UNUSED(ignored))
 {
     return _PyType_GetTextSignatureFromInternalDoc(wp->descr->d_base->name, wp->descr->d_base->doc);
 }
 
 static PyObject *
-wrapper_qualname(wrapperobject *wp)
+wrapper_qualname(wrapperobject *wp, void *Py_UNUSED(ignored))
 {
-    return descr_get_qualname((PyDescrObject *)wp->descr);
+    return descr_get_qualname((PyDescrObject *)wp->descr, NULL);
 }
 
 static PyGetSetDef wrapper_getsets[] = {
index 2cce40f..03bdf79 100644 (file)
@@ -181,7 +181,7 @@ static PyMethodDef BaseException_methods[] = {
 };
 
 static PyObject *
-BaseException_get_args(PyBaseExceptionObject *self)
+BaseException_get_args(PyBaseExceptionObject *self, void *Py_UNUSED(ignored))
 {
     if (self->args == NULL) {
         Py_RETURN_NONE;
@@ -191,7 +191,7 @@ BaseException_get_args(PyBaseExceptionObject *self)
 }
 
 static int
-BaseException_set_args(PyBaseExceptionObject *self, PyObject *val)
+BaseException_set_args(PyBaseExceptionObject *self, PyObject *val, void *Py_UNUSED(ignored))
 {
     PyObject *seq;
     if (val == NULL) {
@@ -206,7 +206,7 @@ BaseException_set_args(PyBaseExceptionObject *self, PyObject *val)
 }
 
 static PyObject *
-BaseException_get_tb(PyBaseExceptionObject *self)
+BaseException_get_tb(PyBaseExceptionObject *self, void *Py_UNUSED(ignored))
 {
     if (self->traceback == NULL) {
         Py_RETURN_NONE;
@@ -216,7 +216,7 @@ BaseException_get_tb(PyBaseExceptionObject *self)
 }
 
 static int
-BaseException_set_tb(PyBaseExceptionObject *self, PyObject *tb)
+BaseException_set_tb(PyBaseExceptionObject *self, PyObject *tb, void *Py_UNUSED(ignored))
 {
     if (tb == NULL) {
         PyErr_SetString(PyExc_TypeError, "__traceback__ may not be deleted");
@@ -234,7 +234,8 @@ BaseException_set_tb(PyBaseExceptionObject *self, PyObject *tb)
 }
 
 static PyObject *
-BaseException_get_context(PyObject *self) {
+BaseException_get_context(PyObject *self, void *Py_UNUSED(ignored))
+{
     PyObject *res = PyException_GetContext(self);
     if (res)
         return res;  /* new reference already returned above */
@@ -242,7 +243,8 @@ BaseException_get_context(PyObject *self) {
 }
 
 static int
-BaseException_set_context(PyObject *self, PyObject *arg) {
+BaseException_set_context(PyObject *self, PyObject *arg, void *Py_UNUSED(ignored))
+{
     if (arg == NULL) {
         PyErr_SetString(PyExc_TypeError, "__context__ may not be deleted");
         return -1;
@@ -261,7 +263,8 @@ BaseException_set_context(PyObject *self, PyObject *arg) {
 }
 
 static PyObject *
-BaseException_get_cause(PyObject *self) {
+BaseException_get_cause(PyObject *self, void *Py_UNUSED(ignored))
+{
     PyObject *res = PyException_GetCause(self);
     if (res)
         return res;  /* new reference already returned above */
@@ -269,7 +272,8 @@ BaseException_get_cause(PyObject *self) {
 }
 
 static int
-BaseException_set_cause(PyObject *self, PyObject *arg) {
+BaseException_set_cause(PyObject *self, PyObject *arg, void *Py_UNUSED(ignored))
+{
     if (arg == NULL) {
         PyErr_SetString(PyExc_TypeError, "__cause__ may not be deleted");
         return -1;
@@ -292,10 +296,10 @@ static PyGetSetDef BaseException_getset[] = {
     {"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict},
     {"args", (getter)BaseException_get_args, (setter)BaseException_set_args},
     {"__traceback__", (getter)BaseException_get_tb, (setter)BaseException_set_tb},
-    {"__context__", (getter)BaseException_get_context,
-     (setter)BaseException_set_context, PyDoc_STR("exception context")},
-    {"__cause__", (getter)BaseException_get_cause,
-     (setter)BaseException_set_cause, PyDoc_STR("exception cause")},
+    {"__context__", BaseException_get_context,
+     BaseException_set_context, PyDoc_STR("exception context")},
+    {"__cause__", BaseException_get_cause,
+     BaseException_set_cause, PyDoc_STR("exception cause")},
     {NULL},
 };
 
@@ -310,7 +314,7 @@ PyException_GetTraceback(PyObject *self) {
 
 int
 PyException_SetTraceback(PyObject *self, PyObject *tb) {
-    return BaseException_set_tb((PyBaseExceptionObject *)self, tb);
+    return BaseException_set_tb((PyBaseExceptionObject *)self, tb, NULL);
 }
 
 PyObject *
index 0f71944..ed4e12b 100644 (file)
@@ -3,7 +3,8 @@
 #define PY_SSIZE_T_CLEAN
 #include "Python.h"
 
-#ifdef HAVE_GETC_UNLOCKED
+#if defined(HAVE_GETC_UNLOCKED) && !defined(_Py_MEMORY_SANITIZER)
+/* clang MemorySanitizer doesn't yet understand getc_unlocked. */
 #define GETC(f) getc_unlocked(f)
 #define FLOCKFILE(f) flockfile(f)
 #define FUNLOCKFILE(f) funlockfile(f)
index f518dc4..4ef10d0 100644 (file)
@@ -64,7 +64,7 @@ frame_getlineno(PyFrameObject *f, void *closure)
  *    that time.
  */
 static int
-frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno)
+frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignored))
 {
     int new_lineno = 0;                 /* The new value of f_lineno */
     long l_new_lineno;
@@ -519,7 +519,7 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg)
     return 0;
 }
 
-static void
+static int
 frame_tp_clear(PyFrameObject *f)
 {
     PyObject **fastlocals, **p, **oldtop;
@@ -547,6 +547,7 @@ frame_tp_clear(PyFrameObject *f)
         for (p = f->f_valuestack; p < oldtop; p++)
             Py_CLEAR(*p);
     }
+    return 0;
 }
 
 static PyObject *
@@ -561,7 +562,7 @@ frame_clear(PyFrameObject *f)
         _PyGen_Finalize(f->f_gen);
         assert(f->f_gen == NULL);
     }
-    frame_tp_clear(f);
+    (void)frame_tp_clear(f);
     Py_RETURN_NONE;
 }
 
index 241685d..413a590 100644 (file)
@@ -242,14 +242,14 @@ static PyMemberDef func_memberlist[] = {
 };
 
 static PyObject *
-func_get_code(PyFunctionObject *op)
+func_get_code(PyFunctionObject *op, void *Py_UNUSED(ignored))
 {
     Py_INCREF(op->func_code);
     return op->func_code;
 }
 
 static int
-func_set_code(PyFunctionObject *op, PyObject *value)
+func_set_code(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored))
 {
     Py_ssize_t nfree, nclosure;
 
@@ -277,14 +277,14 @@ func_set_code(PyFunctionObject *op, PyObject *value)
 }
 
 static PyObject *
-func_get_name(PyFunctionObject *op)
+func_get_name(PyFunctionObject *op, void *Py_UNUSED(ignored))
 {
     Py_INCREF(op->func_name);
     return op->func_name;
 }
 
 static int
-func_set_name(PyFunctionObject *op, PyObject *value)
+func_set_name(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored))
 {
     /* Not legal to del f.func_name or to set it to anything
      * other than a string object. */
@@ -299,14 +299,14 @@ func_set_name(PyFunctionObject *op, PyObject *value)
 }
 
 static PyObject *
-func_get_qualname(PyFunctionObject *op)
+func_get_qualname(PyFunctionObject *op, void *Py_UNUSED(ignored))
 {
     Py_INCREF(op->func_qualname);
     return op->func_qualname;
 }
 
 static int
-func_set_qualname(PyFunctionObject *op, PyObject *value)
+func_set_qualname(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored))
 {
     /* Not legal to del f.__qualname__ or to set it to anything
      * other than a string object. */
@@ -321,7 +321,7 @@ func_set_qualname(PyFunctionObject *op, PyObject *value)
 }
 
 static PyObject *
-func_get_defaults(PyFunctionObject *op)
+func_get_defaults(PyFunctionObject *op, void *Py_UNUSED(ignored))
 {
     if (op->func_defaults == NULL) {
         Py_RETURN_NONE;
@@ -331,7 +331,7 @@ func_get_defaults(PyFunctionObject *op)
 }
 
 static int
-func_set_defaults(PyFunctionObject *op, PyObject *value)
+func_set_defaults(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored))
 {
     /* Legal to del f.func_defaults.
      * Can only set func_defaults to NULL or a tuple. */
@@ -348,7 +348,7 @@ func_set_defaults(PyFunctionObject *op, PyObject *value)
 }
 
 static PyObject *
-func_get_kwdefaults(PyFunctionObject *op)
+func_get_kwdefaults(PyFunctionObject *op, void *Py_UNUSED(ignored))
 {
     if (op->func_kwdefaults == NULL) {
         Py_RETURN_NONE;
@@ -358,7 +358,7 @@ func_get_kwdefaults(PyFunctionObject *op)
 }
 
 static int
-func_set_kwdefaults(PyFunctionObject *op, PyObject *value)
+func_set_kwdefaults(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored))
 {
     if (value == Py_None)
         value = NULL;
@@ -375,7 +375,7 @@ func_set_kwdefaults(PyFunctionObject *op, PyObject *value)
 }
 
 static PyObject *
-func_get_annotations(PyFunctionObject *op)
+func_get_annotations(PyFunctionObject *op, void *Py_UNUSED(ignored))
 {
     if (op->func_annotations == NULL) {
         op->func_annotations = PyDict_New();
@@ -387,7 +387,7 @@ func_get_annotations(PyFunctionObject *op)
 }
 
 static int
-func_set_annotations(PyFunctionObject *op, PyObject *value)
+func_set_annotations(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored))
 {
     if (value == Py_None)
         value = NULL;
index e91d111..b0e8770 100644 (file)
@@ -644,14 +644,14 @@ gen_repr(PyGenObject *gen)
 }
 
 static PyObject *
-gen_get_name(PyGenObject *op)
+gen_get_name(PyGenObject *op, void *Py_UNUSED(ignored))
 {
     Py_INCREF(op->gi_name);
     return op->gi_name;
 }
 
 static int
-gen_set_name(PyGenObject *op, PyObject *value)
+gen_set_name(PyGenObject *op, PyObject *value, void *Py_UNUSED(ignored))
 {
     /* Not legal to del gen.gi_name or to set it to anything
      * other than a string object. */
@@ -666,14 +666,14 @@ gen_set_name(PyGenObject *op, PyObject *value)
 }
 
 static PyObject *
-gen_get_qualname(PyGenObject *op)
+gen_get_qualname(PyGenObject *op, void *Py_UNUSED(ignored))
 {
     Py_INCREF(op->gi_qualname);
     return op->gi_qualname;
 }
 
 static int
-gen_set_qualname(PyGenObject *op, PyObject *value)
+gen_set_qualname(PyGenObject *op, PyObject *value, void *Py_UNUSED(ignored))
 {
     /* Not legal to del gen.__qualname__ or to set it to anything
      * other than a string object. */
@@ -688,7 +688,7 @@ gen_set_qualname(PyGenObject *op, PyObject *value)
 }
 
 static PyObject *
-gen_getyieldfrom(PyGenObject *gen)
+gen_getyieldfrom(PyGenObject *gen, void *Py_UNUSED(ignored))
 {
     PyObject *yf = _PyGen_yf(gen);
     if (yf == NULL)
@@ -927,7 +927,7 @@ coro_await(PyCoroObject *coro)
 }
 
 static PyObject *
-coro_get_cr_await(PyCoroObject *coro)
+coro_get_cr_await(PyCoroObject *coro, void *Py_UNUSED(ignored))
 {
     PyObject *yf = _PyGen_yf((PyGenObject *) coro);
     if (yf == NULL)
@@ -1166,11 +1166,11 @@ PyCoro_New(PyFrameObject *f, PyObject *name, PyObject *qualname)
         ((PyCoroObject *)coro)->cr_origin = NULL;
     } else {
         PyObject *cr_origin = compute_cr_origin(origin_depth);
+        ((PyCoroObject *)coro)->cr_origin = cr_origin;
         if (!cr_origin) {
             Py_DECREF(coro);
             return NULL;
         }
-        ((PyCoroObject *)coro)->cr_origin = cr_origin;
     }
 
     return coro;
index ccf45ff..ce8fa3f 100644 (file)
@@ -2905,7 +2905,7 @@ _IntTupleFromSsizet(int len, Py_ssize_t *vals)
 }
 
 static PyObject *
-memory_obj_get(PyMemoryViewObject *self)
+memory_obj_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
 {
     Py_buffer *view = &self->view;
 
@@ -2918,56 +2918,56 @@ memory_obj_get(PyMemoryViewObject *self)
 }
 
 static PyObject *
-memory_nbytes_get(PyMemoryViewObject *self)
+memory_nbytes_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
 {
     CHECK_RELEASED(self);
     return PyLong_FromSsize_t(self->view.len);
 }
 
 static PyObject *
-memory_format_get(PyMemoryViewObject *self)
+memory_format_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
 {
     CHECK_RELEASED(self);
     return PyUnicode_FromString(self->view.format);
 }
 
 static PyObject *
-memory_itemsize_get(PyMemoryViewObject *self)
+memory_itemsize_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
 {
     CHECK_RELEASED(self);
     return PyLong_FromSsize_t(self->view.itemsize);
 }
 
 static PyObject *
-memory_shape_get(PyMemoryViewObject *self)
+memory_shape_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
 {
     CHECK_RELEASED(self);
     return _IntTupleFromSsizet(self->view.ndim, self->view.shape);
 }
 
 static PyObject *
-memory_strides_get(PyMemoryViewObject *self)
+memory_strides_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
 {
     CHECK_RELEASED(self);
     return _IntTupleFromSsizet(self->view.ndim, self->view.strides);
 }
 
 static PyObject *
-memory_suboffsets_get(PyMemoryViewObject *self)
+memory_suboffsets_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
 {
     CHECK_RELEASED(self);
     return _IntTupleFromSsizet(self->view.ndim, self->view.suboffsets);
 }
 
 static PyObject *
-memory_readonly_get(PyMemoryViewObject *self)
+memory_readonly_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
 {
     CHECK_RELEASED(self);
     return PyBool_FromLong(self->view.readonly);
 }
 
 static PyObject *
-memory_ndim_get(PyMemoryViewObject *self)
+memory_ndim_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
 {
     CHECK_RELEASED(self);
     return PyLong_FromLong(self->view.ndim);
index 2cf3146..f9bac19 100644 (file)
@@ -103,16 +103,13 @@ meth_dealloc(PyCFunctionObject *m)
 static PyObject *
 meth_reduce(PyCFunctionObject *m)
 {
-    PyObject *builtins;
-    PyObject *getattr;
     _Py_IDENTIFIER(getattr);
 
     if (m->m_self == NULL || PyModule_Check(m->m_self))
         return PyUnicode_FromString(m->m_ml->ml_name);
 
-    builtins = PyEval_GetBuiltins();
-    getattr = _PyDict_GetItemId(builtins, &PyId_getattr);
-    return Py_BuildValue("O(Os)", getattr, m->m_self, m->m_ml->ml_name);
+    return Py_BuildValue("N(Os)", _PyEval_GetBuiltinId(&PyId_getattr),
+                         m->m_self, m->m_ml->ml_name);
 }
 
 static PyMethodDef meth_methods[] = {
index 7de102e..c2f9d30 100644 (file)
@@ -103,15 +103,15 @@ namespace_repr(PyObject *ns)
             PyObject *value, *item;
 
             value = PyDict_GetItem(d, key);
-            assert(value != NULL);
-
-            item = PyUnicode_FromFormat("%S=%R", key, value);
-            if (item == NULL) {
-                loop_error = 1;
-            }
-            else {
-                loop_error = PyList_Append(pairs, item);
-                Py_DECREF(item);
+            if (value != NULL) {
+                item = PyUnicode_FromFormat("%S=%R", key, value);
+                if (item == NULL) {
+                    loop_error = 1;
+                }
+                else {
+                    loop_error = PyList_Append(pairs, item);
+                    Py_DECREF(item);
+                }
             }
         }
 
index 5535b7e..138df44 100644 (file)
@@ -411,34 +411,71 @@ _Py_BreakPoint(void)
 }
 
 
+/* Heuristic checking if the object memory has been deallocated.
+   Rely on the debug hooks on Python memory allocators which fills the memory
+   with DEADBYTE (0xDB) when memory is deallocated.
+
+   The function can be used to prevent segmentation fault on dereferencing
+   pointers like 0xdbdbdbdbdbdbdbdb. Such pointer is very unlikely to be mapped
+   in memory. */
+int
+_PyObject_IsFreed(PyObject *op)
+{
+    uintptr_t ptr = (uintptr_t)op;
+    if (_PyMem_IsFreed(&ptr, sizeof(ptr))) {
+        return 1;
+    }
+    int freed = _PyMem_IsFreed(&op->ob_type, sizeof(op->ob_type));
+    /* ignore op->ob_ref: the value can have be modified
+       by Py_INCREF() and Py_DECREF(). */
+#ifdef Py_TRACE_REFS
+    freed &= _PyMem_IsFreed(&op->_ob_next, sizeof(op->_ob_next));
+    freed &= _PyMem_IsFreed(&op->_ob_prev, sizeof(op->_ob_prev));
+#endif
+    return freed;
+}
+
+
 /* For debugging convenience.  See Misc/gdbinit for some useful gdb hooks */
 void
 _PyObject_Dump(PyObject* op)
 {
-    if (op == NULL)
-        fprintf(stderr, "NULL\n");
-    else {
-        PyGILState_STATE gil;
-        PyObject *error_type, *error_value, *error_traceback;
-
-        fprintf(stderr, "object  : ");
-        gil = PyGILState_Ensure();
-
-        PyErr_Fetch(&error_type, &error_value, &error_traceback);
-        (void)PyObject_Print(op, stderr, 0);
-        PyErr_Restore(error_type, error_value, error_traceback);
-
-        PyGILState_Release(gil);
-        /* XXX(twouters) cast refcount to long until %zd is
-           universally available */
-        fprintf(stderr, "\n"
-            "type    : %s\n"
-            "refcount: %ld\n"
-            "address : %p\n",
-            Py_TYPE(op)==NULL ? "NULL" : Py_TYPE(op)->tp_name,
-            (long)op->ob_refcnt,
-            op);
+    if (op == NULL) {
+        fprintf(stderr, "<NULL object>\n");
+        fflush(stderr);
+        return;
+    }
+
+    if (_PyObject_IsFreed(op)) {
+        /* It seems like the object memory has been freed:
+           don't access it to prevent a segmentation fault. */
+        fprintf(stderr, "<freed object>\n");
+        return;
     }
+
+    PyGILState_STATE gil;
+    PyObject *error_type, *error_value, *error_traceback;
+
+    fprintf(stderr, "object  : ");
+    fflush(stderr);
+    gil = PyGILState_Ensure();
+
+    PyErr_Fetch(&error_type, &error_value, &error_traceback);
+    (void)PyObject_Print(op, stderr, 0);
+    fflush(stderr);
+    PyErr_Restore(error_type, error_value, error_traceback);
+
+    PyGILState_Release(gil);
+    /* XXX(twouters) cast refcount to long until %zd is
+       universally available */
+    fprintf(stderr, "\n"
+        "type    : %s\n"
+        "refcount: %ld\n"
+        "address : %p\n",
+        Py_TYPE(op)==NULL ? "NULL" : Py_TYPE(op)->tp_name,
+        (long)op->ob_refcnt,
+        op);
+    fflush(stderr);
 }
 
 PyObject *
index 0e485d6..3b0c35b 100644 (file)
@@ -29,19 +29,36 @@ static void _PyMem_DebugCheckAddress(char api_id, const void *p);
 static void _PyMem_SetupDebugHooksDomain(PyMemAllocatorDomain domain);
 
 #if defined(__has_feature)  /* Clang */
- #if __has_feature(address_sanitizer)  /* is ASAN enabled? */
-  #define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS \
+#  if __has_feature(address_sanitizer) /* is ASAN enabled? */
+#    define _Py_NO_ADDRESS_SAFETY_ANALYSIS \
         __attribute__((no_address_safety_analysis))
- #else
-  #define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS
- #endif
-#else
- #if defined(__SANITIZE_ADDRESS__)  /* GCC 4.8.x, is ASAN enabled? */
-  #define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS \
+#  endif
+#  if __has_feature(thread_sanitizer)  /* is TSAN enabled? */
+#    define _Py_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
+#  endif
+#  if __has_feature(memory_sanitizer)  /* is MSAN enabled? */
+#    define _Py_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory))
+#  endif
+#elif defined(__GNUC__)
+#  if defined(__SANITIZE_ADDRESS__)    /* GCC 4.8+, is ASAN enabled? */
+#    define _Py_NO_ADDRESS_SAFETY_ANALYSIS \
         __attribute__((no_address_safety_analysis))
- #else
-  #define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS
- #endif
+#  endif
+   // TSAN is supported since GCC 4.8, but __SANITIZE_THREAD__ macro
+   // is provided only since GCC 7.
+#  if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
+#    define _Py_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
+#  endif
+#endif
+
+#ifndef _Py_NO_ADDRESS_SAFETY_ANALYSIS
+#  define _Py_NO_ADDRESS_SAFETY_ANALYSIS
+#endif
+#ifndef _Py_NO_SANITIZE_THREAD
+#  define _Py_NO_SANITIZE_THREAD
+#endif
+#ifndef _Py_NO_SANITIZE_MEMORY
+#  define _Py_NO_SANITIZE_MEMORY
 #endif
 
 #ifdef WITH_PYMALLOC
@@ -1327,7 +1344,9 @@ obmalloc controls.  Since this test is needed at every entry point, it's
 extremely desirable that it be this fast.
 */
 
-static bool ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS
+static bool _Py_NO_ADDRESS_SAFETY_ANALYSIS
+            _Py_NO_SANITIZE_THREAD
+            _Py_NO_SANITIZE_MEMORY
 address_in_range(void *p, poolp pool)
 {
     // Since address_in_range may be reading from memory which was not allocated
@@ -2072,6 +2091,22 @@ _PyMem_DebugRawCalloc(void *ctx, size_t nelem, size_t elsize)
 }
 
 
+/* Heuristic checking if the memory has been freed. Rely on the debug hooks on
+   Python memory allocators which fills the memory with DEADBYTE (0xDB) when
+   memory is deallocated. */
+int
+_PyMem_IsFreed(void *ptr, size_t size)
+{
+    unsigned char *bytes = ptr;
+    for (size_t i=0; i < size; i++) {
+        if (bytes[i] != DEADBYTE) {
+            return 0;
+        }
+    }
+    return 1;
+}
+
+
 /* The debug free first checks the 2*SST bytes on each end for sanity (in
    particular, that the FORBIDDENBYTEs with the api ID are still intact).
    Then fills the original bytes with DEADBYTE.
index 5aa51be..7487bd9 100644 (file)
@@ -101,10 +101,6 @@ Others:
 * _odict_find_node(od, key)
 * _odict_keys_equal(od1, od2)
 
-Used, but specific to the linked-list implementation:
-
-* _odict_free_fast_nodes(od)
-
 And here's a look at how the linked-list relates to the OrderedDict API:
 
 ============ === === ==== ==== ==== === ==== ===== ==== ==== === ==== === ===
@@ -378,7 +374,6 @@ tp_iter           odict_iter
 tp_dictoffset     (offset)
 tp_init           odict_init
 tp_alloc          (repeated)
-tp_new            odict_new
 ================= ================
 
 ================= ================
@@ -530,15 +525,6 @@ struct _odictnode {
 #define _odict_FOREACH(od, node) \
     for (node = _odict_FIRST(od); node != NULL; node = _odictnode_NEXT(node))
 
-#define _odict_FAST_SIZE(od) ((PyDictObject *)od)->ma_keys->dk_size
-
-static void
-_odict_free_fast_nodes(PyODictObject *od) {
-    if (od->od_fast_nodes) {
-        PyMem_FREE(od->od_fast_nodes);
-    }
-}
-
 /* Return the index into the hash table, regardless of a valid node. */
 static Py_ssize_t
 _odict_get_index_raw(PyODictObject *od, PyObject *key, Py_hash_t hash)
@@ -559,7 +545,8 @@ _odict_get_index_raw(PyODictObject *od, PyObject *key, Py_hash_t hash)
 
 /* Replace od->od_fast_nodes with a new table matching the size of dict's. */
 static int
-_odict_resize(PyODictObject *od) {
+_odict_resize(PyODictObject *od)
+{
     Py_ssize_t size, i;
     _ODictNode **fast_nodes, *node;
 
@@ -585,7 +572,7 @@ _odict_resize(PyODictObject *od) {
     }
 
     /* Replace the old fast nodes table. */
-    _odict_free_fast_nodes(od);
+    PyMem_FREE(od->od_fast_nodes);
     od->od_fast_nodes = fast_nodes;
     od->od_fast_nodes_size = size;
     od->od_resize_sentinel = ((PyDictObject *)od)->ma_keys;
@@ -623,6 +610,7 @@ _odict_find_node_hash(PyODictObject *od, PyObject *key, Py_hash_t hash)
     index = _odict_get_index(od, key, hash);
     if (index < 0)
         return NULL;
+    assert(od->od_fast_nodes != NULL);
     return od->od_fast_nodes[index];
 }
 
@@ -640,6 +628,7 @@ _odict_find_node(PyODictObject *od, PyObject *key)
     index = _odict_get_index(od, key, hash);
     if (index < 0)
         return NULL;
+    assert(od->od_fast_nodes != NULL);
     return od->od_fast_nodes[index];
 }
 
@@ -684,7 +673,8 @@ _odict_add_new_node(PyODictObject *od, PyObject *key, Py_hash_t hash)
         Py_DECREF(key);
         return -1;
     }
-    else if (od->od_fast_nodes[i] != NULL) {
+    assert(od->od_fast_nodes != NULL);
+    if (od->od_fast_nodes[i] != NULL) {
         /* We already have a node for the key so there's no need to add one. */
         Py_DECREF(key);
         return 0;
@@ -763,6 +753,7 @@ _odict_clear_node(PyODictObject *od, _ODictNode *node, PyObject *key,
     if (i < 0)
         return PyErr_Occurred() ? -1 : 0;
 
+    assert(od->od_fast_nodes != NULL);
     if (node == NULL)
         node = od->od_fast_nodes[i];
     assert(node == od->od_fast_nodes[i]);
@@ -783,8 +774,10 @@ _odict_clear_nodes(PyODictObject *od)
 {
     _ODictNode *node, *next;
 
-    _odict_free_fast_nodes(od);
+    PyMem_FREE(od->od_fast_nodes);
     od->od_fast_nodes = NULL;
+    od->od_fast_nodes_size = 0;
+    od->od_resize_sentinel = NULL;
 
     node = _odict_FIRST(od);
     _odict_FIRST(od) = NULL;
@@ -887,7 +880,7 @@ static PyObject *
 odict_sizeof(PyODictObject *od)
 {
     Py_ssize_t res = _PyDict_SizeOf((PyDictObject *)od);
-    res += sizeof(_ODictNode *) * _odict_FAST_SIZE(od);  /* od_fast_nodes */
+    res += sizeof(_ODictNode *) * od->od_fast_nodes_size;  /* od_fast_nodes */
     if (!_odict_EMPTY(od)) {
         res += sizeof(_ODictNode) * PyODict_SIZE(od);  /* linked-list */
     }
@@ -1175,12 +1168,10 @@ PyDoc_STRVAR(odict_clear__doc__,
              "od.clear() -> None.  Remove all items from od.");
 
 static PyObject *
-odict_clear(register PyODictObject *od)
+odict_clear(register PyODictObject *od, PyObject *Py_UNUSED(ignored))
 {
     PyDict_Clear((PyObject *)od);
     _odict_clear_nodes(od);
-    if (_odict_resize(od) < 0)
-        return NULL;
     Py_RETURN_NONE;
 }
 
@@ -1484,13 +1475,10 @@ odict_traverse(PyODictObject *od, visitproc visit, void *arg)
 static int
 odict_tp_clear(PyODictObject *od)
 {
-    PyObject *res;
     Py_CLEAR(od->od_inst_dict);
     Py_CLEAR(od->od_weakreflist);
-    res = odict_clear(od);
-    if (res == NULL)
-        return -1;
-    Py_DECREF(res);
+    PyDict_Clear((PyObject *)od);
+    _odict_clear_nodes(od);
     return 0;
 }
 
@@ -1565,27 +1553,6 @@ odict_init(PyObject *self, PyObject *args, PyObject *kwds)
     }
 }
 
-/* tp_new */
-
-static PyObject *
-odict_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
-{
-    PyODictObject *od;
-
-    od = (PyODictObject *)PyDict_Type.tp_new(type, args, kwds);
-    if (od == NULL)
-        return NULL;
-
-    /* type constructor fills the memory with zeros (see
-       PyType_GenericAlloc()), there is no need to set them to zero again */
-    if (_odict_resize(od) < 0) {
-        Py_DECREF(od);
-        return NULL;
-    }
-
-    return (PyObject*)od;
-}
-
 /* PyODict_Type */
 
 PyTypeObject PyODict_Type = {
@@ -1626,7 +1593,7 @@ PyTypeObject PyODict_Type = {
     offsetof(PyODictObject, od_inst_dict),      /* tp_dictoffset */
     (initproc)odict_init,                       /* tp_init */
     PyType_GenericAlloc,                        /* tp_alloc */
-    (newfunc)odict_new,                         /* tp_new */
+    0,                                          /* tp_new */
     0,                                          /* tp_free */
 };
 
@@ -1636,8 +1603,9 @@ PyTypeObject PyODict_Type = {
  */
 
 PyObject *
-PyODict_New(void) {
-    return odict_new(&PyODict_Type, NULL, NULL);
+PyODict_New(void)
+{
+    return PyDict_Type.tp_new(&PyODict_Type, NULL, NULL);
 }
 
 static int
@@ -1835,40 +1803,21 @@ done:
 PyDoc_STRVAR(reduce_doc, "Return state information for pickling");
 
 static PyObject *
-odictiter_reduce(odictiterobject *di)
+odictiter_reduce(odictiterobject *di, PyObject *Py_UNUSED(ignored))
 {
-    PyObject *list, *iter;
-
-    list = PyList_New(0);
-    if (!list)
-        return NULL;
+    /* copy the iterator state */
+    odictiterobject tmp = *di;
+    Py_XINCREF(tmp.di_odict);
+    Py_XINCREF(tmp.di_current);
 
     /* iterate the temporary into a list */
-    for(;;) {
-        PyObject *element = odictiter_iternext(di);
-        if (element) {
-            if (PyList_Append(list, element)) {
-                Py_DECREF(element);
-                Py_DECREF(list);
-                return NULL;
-            }
-            Py_DECREF(element);
-        }
-        else {
-            /* done iterating? */
-            break;
-        }
-    }
-    if (PyErr_Occurred()) {
-        Py_DECREF(list);
-        return NULL;
-    }
-    iter = _PyObject_GetBuiltin("iter");
-    if (iter == NULL) {
-        Py_DECREF(list);
+    PyObject *list = PySequence_List((PyObject*)&tmp);
+    Py_XDECREF(tmp.di_odict);
+    Py_XDECREF(tmp.di_current);
+    if (list == NULL) {
         return NULL;
     }
-    return Py_BuildValue("N(N)", iter, list);
+    return Py_BuildValue("N(N)", _PyObject_GetBuiltin("iter"), list);
 }
 
 static PyMethodDef odictiter_methods[] = {
index e4f778b..037be87 100644 (file)
@@ -1154,6 +1154,7 @@ long_range:
     it = PyObject_New(longrangeiterobject, &PyLongRangeIter_Type);
     if (it == NULL)
         return NULL;
+    it->index = it->start = it->step = NULL;
 
     /* start + (len - 1) * step */
     it->len = range->length;
index ce35aa2..357e48c 100644 (file)
@@ -302,7 +302,6 @@ actually be smaller than the old one.
 static int
 set_table_resize(PySetObject *so, Py_ssize_t minused)
 {
-    Py_ssize_t newsize;
     setentry *oldtable, *newtable, *entry;
     Py_ssize_t oldmask = so->mask;
     size_t newmask;
@@ -313,13 +312,9 @@ set_table_resize(PySetObject *so, Py_ssize_t minused)
 
     /* Find the smallest table size > minused. */
     /* XXX speed-up with intrinsics */
-    for (newsize = PySet_MINSIZE;
-         newsize <= minused && newsize > 0;
-         newsize <<= 1)
-        ;
-    if (newsize <= 0) {
-        PyErr_NoMemory();
-        return -1;
+    size_t newsize = PySet_MINSIZE;
+    while (newsize <= (size_t)minused) {
+        newsize <<= 1; // The largest possible value is PY_SSIZE_T_MAX + 1.
     }
 
     /* Get space for a new table. */
index df501ed..31fed34 100644 (file)
@@ -1,28 +1,24 @@
-/* stringlib: locale related helpers implementation */
-
-#include <locale.h>
-
-#if !STRINGLIB_IS_UNICODE
-#   error "localeutil.h is specific to Unicode"
-#endif
+/* _PyUnicode_InsertThousandsGrouping() helper functions */
 
 typedef struct {
     const char *grouping;
     char previous;
     Py_ssize_t i; /* Where we're currently pointing in grouping. */
-} STRINGLIB(GroupGenerator);
+} GroupGenerator;
+
 
 static void
-STRINGLIB(GroupGenerator_init)(STRINGLIB(GroupGenerator) *self, const char *grouping)
+GroupGenerator_init(GroupGenerator *self, const char *grouping)
 {
     self->grouping = grouping;
     self->i = 0;
     self->previous = 0;
 }
 
+
 /* Returns the next grouping, or 0 to signify end. */
 static Py_ssize_t
-STRINGLIB(GroupGenerator_next)(STRINGLIB(GroupGenerator) *self)
+GroupGenerator_next(GroupGenerator *self)
 {
     /* Note that we don't really do much error checking here. If a
        grouping string contains just CHAR_MAX, for example, then just
@@ -43,138 +39,44 @@ STRINGLIB(GroupGenerator_next)(STRINGLIB(GroupGenerator) *self)
     }
 }
 
+
 /* Fill in some digits, leading zeros, and thousands separator. All
    are optional, depending on when we're called. */
 static void
-STRINGLIB(fill)(STRINGLIB_CHAR **digits_end, STRINGLIB_CHAR **buffer_end,
-     Py_ssize_t n_chars, Py_ssize_t n_zeros, STRINGLIB_CHAR* thousands_sep,
-     Py_ssize_t thousands_sep_len)
+InsertThousandsGrouping_fill(_PyUnicodeWriter *writer, Py_ssize_t *buffer_pos,
+                             PyObject *digits, Py_ssize_t *digits_pos,
+                             Py_ssize_t n_chars, Py_ssize_t n_zeros,
+                             PyObject *thousands_sep, Py_ssize_t thousands_sep_len,
+                             Py_UCS4 *maxchar)
 {
-    Py_ssize_t i;
+    if (!writer) {
+        /* if maxchar > 127, maxchar is already set */
+        if (*maxchar == 127 && thousands_sep) {
+            Py_UCS4 maxchar2 = PyUnicode_MAX_CHAR_VALUE(thousands_sep);
+            *maxchar = Py_MAX(*maxchar, maxchar2);
+        }
+        return;
+    }
 
     if (thousands_sep) {
-        *buffer_end -= thousands_sep_len;
+        *buffer_pos -= thousands_sep_len;
 
         /* Copy the thousands_sep chars into the buffer. */
-        memcpy(*buffer_end, thousands_sep,
-               thousands_sep_len * STRINGLIB_SIZEOF_CHAR);
-    }
-
-    *buffer_end -= n_chars;
-    *digits_end -= n_chars;
-    memcpy(*buffer_end, *digits_end, n_chars * sizeof(STRINGLIB_CHAR));
-
-    *buffer_end -= n_zeros;
-    for (i = 0; i < n_zeros; i++)
-        (*buffer_end)[i] = '0';
-}
-
-/**
- * InsertThousandsGrouping:
- * @buffer: A pointer to the start of a string.
- * @n_buffer: Number of characters in @buffer.
- * @digits: A pointer to the digits we're reading from. If count
- *          is non-NULL, this is unused.
- * @n_digits: The number of digits in the string, in which we want
- *            to put the grouping chars.
- * @min_width: The minimum width of the digits in the output string.
- *             Output will be zero-padded on the left to fill.
- * @grouping: see definition in localeconv().
- * @thousands_sep: see definition in localeconv().
- *
- * There are 2 modes: counting and filling. If @buffer is NULL,
- *  we are in counting mode, else filling mode.
- * If counting, the required buffer size is returned.
- * If filling, we know the buffer will be large enough, so we don't
- *  need to pass in the buffer size.
- * Inserts thousand grouping characters (as defined by grouping and
- *  thousands_sep) into the string between buffer and buffer+n_digits.
- *
- * Return value: 0 on error, else 1.  Note that no error can occur if
- *  count is non-NULL.
- *
- * This name won't be used, the includer of this file should define
- *  it to be the actual function name, based on unicode or string.
- *
- * As closely as possible, this code mimics the logic in decimal.py's
-    _insert_thousands_sep().
- **/
-static Py_ssize_t
-STRINGLIB(InsertThousandsGrouping)(
-    STRINGLIB_CHAR *buffer,
-    Py_ssize_t n_buffer,
-    STRINGLIB_CHAR *digits,
-    Py_ssize_t n_digits,
-    Py_ssize_t min_width,
-    const char *grouping,
-    STRINGLIB_CHAR *thousands_sep,
-    Py_ssize_t thousands_sep_len)
-{
-    Py_ssize_t count = 0;
-    Py_ssize_t n_zeros;
-    int loop_broken = 0;
-    int use_separator = 0; /* First time through, don't append the
-                              separator. They only go between
-                              groups. */
-    STRINGLIB_CHAR *buffer_end = NULL;
-    STRINGLIB_CHAR *digits_end = NULL;
-    Py_ssize_t l;
-    Py_ssize_t n_chars;
-    Py_ssize_t remaining = n_digits; /* Number of chars remaining to
-                                        be looked at */
-    /* A generator that returns all of the grouping widths, until it
-       returns 0. */
-    STRINGLIB(GroupGenerator) groupgen;
-    STRINGLIB(GroupGenerator_init)(&groupgen, grouping);
-
-    if (buffer) {
-        buffer_end = buffer + n_buffer;
-        digits_end = digits + n_digits;
-    }
-
-    while ((l = STRINGLIB(GroupGenerator_next)(&groupgen)) > 0) {
-        l = Py_MIN(l, Py_MAX(Py_MAX(remaining, min_width), 1));
-        n_zeros = Py_MAX(0, l - remaining);
-        n_chars = Py_MAX(0, Py_MIN(remaining, l));
-
-        /* Use n_zero zero's and n_chars chars */
-
-        /* Count only, don't do anything. */
-        count += (use_separator ? thousands_sep_len : 0) + n_zeros + n_chars;
-
-        if (buffer) {
-            /* Copy into the output buffer. */
-            STRINGLIB(fill)(&digits_end, &buffer_end, n_chars, n_zeros,
-                 use_separator ? thousands_sep : NULL, thousands_sep_len);
-        }
-
-        /* Use a separator next time. */
-        use_separator = 1;
-
-        remaining -= n_chars;
-        min_width -= l;
-
-        if (remaining <= 0 && min_width <= 0) {
-            loop_broken = 1;
-            break;
-        }
-        min_width -= thousands_sep_len;
+        _PyUnicode_FastCopyCharacters(writer->buffer, *buffer_pos,
+                                      thousands_sep, 0,
+                                      thousands_sep_len);
     }
-    if (!loop_broken) {
-        /* We left the loop without using a break statement. */
 
-        l = Py_MAX(Py_MAX(remaining, min_width), 1);
-        n_zeros = Py_MAX(0, l - remaining);
-        n_chars = Py_MAX(0, Py_MIN(remaining, l));
-
-        /* Use n_zero zero's and n_chars chars */
-        count += (use_separator ? thousands_sep_len : 0) + n_zeros + n_chars;
-        if (buffer) {
-            /* Copy into the output buffer. */
-            STRINGLIB(fill)(&digits_end, &buffer_end, n_chars, n_zeros,
-                 use_separator ? thousands_sep : NULL, thousands_sep_len);
-        }
+    *buffer_pos -= n_chars;
+    *digits_pos -= n_chars;
+    _PyUnicode_FastCopyCharacters(writer->buffer, *buffer_pos,
+                                  digits, *digits_pos,
+                                  n_chars);
+
+    if (n_zeros) {
+        *buffer_pos -= n_zeros;
+        enum PyUnicode_Kind kind = PyUnicode_KIND(writer->buffer);
+        void *data = PyUnicode_DATA(writer->buffer);
+        FILL(kind, data, '0', *buffer_pos, n_zeros);
     }
-    return count;
 }
-
index b9e69bf..4682105 100644 (file)
@@ -6794,7 +6794,7 @@ static slotdef slotdefs[] = {
            "__repr__($self, /)\n--\n\nReturn repr(self)."),
     TPSLOT("__hash__", tp_hash, slot_tp_hash, wrap_hashfunc,
            "__hash__($self, /)\n--\n\nReturn hash(self)."),
-    FLSLOT("__call__", tp_call, slot_tp_call, (wrapperfunc)wrap_call,
+    FLSLOT("__call__", tp_call, slot_tp_call, (wrapperfunc)(void(*)(void))wrap_call,
            "__call__($self, /, *args, **kwargs)\n--\n\nCall self as a function.",
            PyWrapperFlag_KEYWORDS),
     TPSLOT("__str__", tp_str, slot_tp_str, wrap_unaryfunc,
@@ -6830,7 +6830,7 @@ static slotdef slotdefs[] = {
     TPSLOT("__delete__", tp_descr_set, slot_tp_descr_set,
            wrap_descr_delete,
            "__delete__($self, instance, /)\n--\n\nDelete an attribute of instance."),
-    FLSLOT("__init__", tp_init, slot_tp_init, (wrapperfunc)wrap_init,
+    FLSLOT("__init__", tp_init, slot_tp_init, (wrapperfunc)(void(*)(void))wrap_init,
            "__init__($self, /, *args, **kwargs)\n--\n\n"
            "Initialize self.  See help(type(self)) for accurate signature.",
            PyWrapperFlag_KEYWORDS),
index fe833a7..d46ab2a 100644 (file)
@@ -218,6 +218,39 @@ static PyObject *unicode_empty = NULL;
         return unicode_empty;                           \
     } while (0)
 
+#define FILL(kind, data, value, start, length) \
+    do { \
+        assert(0 <= start); \
+        assert(kind != PyUnicode_WCHAR_KIND); \
+        switch (kind) { \
+        case PyUnicode_1BYTE_KIND: { \
+            assert(value <= 0xff); \
+            Py_UCS1 ch = (unsigned char)value; \
+            Py_UCS1 *to = (Py_UCS1 *)data + start; \
+            memset(to, ch, length); \
+            break; \
+        } \
+        case PyUnicode_2BYTE_KIND: { \
+            assert(value <= 0xffff); \
+            Py_UCS2 ch = (Py_UCS2)value; \
+            Py_UCS2 *to = (Py_UCS2 *)data + start; \
+            const Py_UCS2 *end = to + length; \
+            for (; to < end; ++to) *to = ch; \
+            break; \
+        } \
+        case PyUnicode_4BYTE_KIND: { \
+            assert(value <= MAX_UNICODE); \
+            Py_UCS4 ch = value; \
+            Py_UCS4 * to = (Py_UCS4 *)data + start; \
+            const Py_UCS4 *end = to + length; \
+            for (; to < end; ++to) *to = ch; \
+            break; \
+        } \
+        default: Py_UNREACHABLE(); \
+        } \
+    } while (0)
+
+
 /* Forward declaration */
 static inline int
 _PyUnicodeWriter_WriteCharInline(_PyUnicodeWriter *writer, Py_UCS4 ch);
@@ -796,7 +829,6 @@ ensure_unicode(PyObject *obj)
 #include "stringlib/count.h"
 #include "stringlib/find.h"
 #include "stringlib/find_max_char.h"
-#include "stringlib/localeutil.h"
 #include "stringlib/undef.h"
 
 #include "stringlib/ucs1lib.h"
@@ -807,7 +839,6 @@ ensure_unicode(PyObject *obj)
 #include "stringlib/find.h"
 #include "stringlib/replace.h"
 #include "stringlib/find_max_char.h"
-#include "stringlib/localeutil.h"
 #include "stringlib/undef.h"
 
 #include "stringlib/ucs2lib.h"
@@ -818,7 +849,6 @@ ensure_unicode(PyObject *obj)
 #include "stringlib/find.h"
 #include "stringlib/replace.h"
 #include "stringlib/find_max_char.h"
-#include "stringlib/localeutil.h"
 #include "stringlib/undef.h"
 
 #include "stringlib/ucs4lib.h"
@@ -829,7 +859,6 @@ ensure_unicode(PyObject *obj)
 #include "stringlib/find.h"
 #include "stringlib/replace.h"
 #include "stringlib/find_max_char.h"
-#include "stringlib/localeutil.h"
 #include "stringlib/undef.h"
 
 #include "stringlib/unicodedefs.h"
@@ -3362,10 +3391,9 @@ unicode_encode_locale(PyObject *unicode, const char *errors,
         return NULL;
     }
 
-    Py_ssize_t wlen2 = wcslen(wstr);
-    if (wlen2 != wlen) {
-        PyMem_Free(wstr);
+    if ((size_t)wlen != wcslen(wstr)) {
         PyErr_SetString(PyExc_ValueError, "embedded null character");
+        PyMem_Free(wstr);
         return NULL;
     }
 
@@ -3374,6 +3402,8 @@ unicode_encode_locale(PyObject *unicode, const char *errors,
     const char *reason;
     int res = _Py_EncodeLocaleEx(wstr, &str, &error_pos, &reason,
                                  current_locale, surrogateescape);
+    PyMem_Free(wstr);
+
     if (res != 0) {
         if (res == -2) {
             PyObject *exc;
@@ -3386,15 +3416,12 @@ unicode_encode_locale(PyObject *unicode, const char *errors,
                 PyCodec_StrictErrors(exc);
                 Py_DECREF(exc);
             }
-            return NULL;
         }
         else {
             PyErr_NoMemory();
-            PyMem_Free(wstr);
-            return NULL;
         }
+        return NULL;
     }
-    PyMem_Free(wstr);
 
     PyObject *bytes = PyBytes_FromString(str);
     PyMem_RawFree(str);
@@ -6042,7 +6069,7 @@ _PyUnicode_DecodeUnicodeEscape(const char *s,
             }
 
             message = "malformed \\N character escape";
-            if (*s == '{') {
+            if (s < end && *s == '{') {
                 const char *start = ++s;
                 size_t namelen;
                 /* look for the closing brace */
@@ -7151,7 +7178,7 @@ decode_code_page_errors(UINT code_page,
                          "in the target code page.";
     /* each step cannot decode more than 1 character, but a character can be
        represented as a surrogate pair */
-    wchar_t buffer[2], *startout, *out;
+    wchar_t buffer[2], *out;
     int insize;
     Py_ssize_t outsize;
     PyObject *errorHandler = NULL;
@@ -7188,7 +7215,7 @@ decode_code_page_errors(UINT code_page,
         *v = (PyObject*)_PyUnicode_New(size * Py_ARRAY_LENGTH(buffer));
         if (*v == NULL)
             goto error;
-        startout = PyUnicode_AS_UNICODE(*v);
+        out = PyUnicode_AS_UNICODE(*v);
     }
     else {
         /* Extend unicode object */
@@ -7199,11 +7226,10 @@ decode_code_page_errors(UINT code_page,
         }
         if (unicode_resize(v, n + size * Py_ARRAY_LENGTH(buffer)) < 0)
             goto error;
-        startout = PyUnicode_AS_UNICODE(*v) + n;
+        out = PyUnicode_AS_UNICODE(*v) + n;
     }
 
     /* Decode the byte string character per character */
-    out = startout;
     while (in < endin)
     {
         /* Decode a character */
@@ -7258,7 +7284,7 @@ decode_code_page_errors(UINT code_page,
     *out = 0;
 
     /* Extend unicode object */
-    outsize = out - startout;
+    outsize = out - PyUnicode_AS_UNICODE(*v);
     assert(outsize <= PyUnicode_WSTR_LENGTH(*v));
     if (unicode_resize(v, outsize) < 0)
         goto error;
@@ -9292,86 +9318,149 @@ any_find_slice(PyObject* s1, PyObject* s2,
     return result;
 }
 
+/* _PyUnicode_InsertThousandsGrouping() helper functions */
+#include "stringlib/localeutil.h"
+
+/**
+ * InsertThousandsGrouping:
+ * @writer: Unicode writer.
+ * @n_buffer: Number of characters in @buffer.
+ * @digits: Digits we're reading from. If count is non-NULL, this is unused.
+ * @d_pos: Start of digits string.
+ * @n_digits: The number of digits in the string, in which we want
+ *            to put the grouping chars.
+ * @min_width: The minimum width of the digits in the output string.
+ *             Output will be zero-padded on the left to fill.
+ * @grouping: see definition in localeconv().
+ * @thousands_sep: see definition in localeconv().
+ *
+ * There are 2 modes: counting and filling. If @writer is NULL,
+ *  we are in counting mode, else filling mode.
+ * If counting, the required buffer size is returned.
+ * If filling, we know the buffer will be large enough, so we don't
+ *  need to pass in the buffer size.
+ * Inserts thousand grouping characters (as defined by grouping and
+ *  thousands_sep) into @writer.
+ *
+ * Return value: -1 on error, number of characters otherwise.
+ **/
 Py_ssize_t
 _PyUnicode_InsertThousandsGrouping(
-    PyObject *unicode, Py_ssize_t index,
+    _PyUnicodeWriter *writer,
     Py_ssize_t n_buffer,
-    void *digits, Py_ssize_t n_digits,
+    PyObject *digits,
+    Py_ssize_t d_pos,
+    Py_ssize_t n_digits,
     Py_ssize_t min_width,
-    const char *grouping, PyObject *thousands_sep,
+    const char *grouping,
+    PyObject *thousands_sep,
     Py_UCS4 *maxchar)
 {
-    unsigned int kind, thousands_sep_kind;
-    char *data, *thousands_sep_data;
-    Py_ssize_t thousands_sep_len;
-    Py_ssize_t len;
-
-    if (unicode != NULL) {
-        kind = PyUnicode_KIND(unicode);
-        data = (char *) PyUnicode_DATA(unicode) + index * kind;
+    if (writer) {
+        assert(digits != NULL);
+        assert(maxchar == NULL);
     }
     else {
-        kind = PyUnicode_1BYTE_KIND;
-        data = NULL;
-    }
-    thousands_sep_kind = PyUnicode_KIND(thousands_sep);
-    thousands_sep_data = PyUnicode_DATA(thousands_sep);
-    thousands_sep_len = PyUnicode_GET_LENGTH(thousands_sep);
-    if (unicode != NULL && thousands_sep_kind != kind) {
-        if (thousands_sep_kind < kind) {
-            thousands_sep_data = _PyUnicode_AsKind(thousands_sep, kind);
-            if (!thousands_sep_data)
-                return -1;
-        }
-        else {
-            data = _PyUnicode_AsKind(unicode, thousands_sep_kind);
-            if (!data)
-                return -1;
+        assert(digits == NULL);
+        assert(maxchar != NULL);
+    }
+    assert(0 <= d_pos);
+    assert(0 <= n_digits);
+    assert(0 <= min_width);
+    assert(grouping != NULL);
+
+    if (digits != NULL) {
+        if (PyUnicode_READY(digits) == -1) {
+            return -1;
         }
     }
+    if (PyUnicode_READY(thousands_sep) == -1) {
+        return -1;
+    }
 
-    switch (kind) {
-    case PyUnicode_1BYTE_KIND:
-        if (unicode != NULL && PyUnicode_IS_ASCII(unicode))
-            len = asciilib_InsertThousandsGrouping(
-                (Py_UCS1 *) data, n_buffer, (Py_UCS1 *) digits, n_digits,
-                min_width, grouping,
-                (Py_UCS1 *) thousands_sep_data, thousands_sep_len);
-        else
-            len = ucs1lib_InsertThousandsGrouping(
-                (Py_UCS1*)data, n_buffer, (Py_UCS1*)digits, n_digits,
-                min_width, grouping,
-                (Py_UCS1 *) thousands_sep_data, thousands_sep_len);
-        break;
-    case PyUnicode_2BYTE_KIND:
-        len = ucs2lib_InsertThousandsGrouping(
-            (Py_UCS2 *) data, n_buffer, (Py_UCS2 *) digits, n_digits,
-            min_width, grouping,
-            (Py_UCS2 *) thousands_sep_data, thousands_sep_len);
-        break;
-    case PyUnicode_4BYTE_KIND:
-        len = ucs4lib_InsertThousandsGrouping(
-            (Py_UCS4 *) data, n_buffer, (Py_UCS4 *) digits, n_digits,
-            min_width, grouping,
-            (Py_UCS4 *) thousands_sep_data, thousands_sep_len);
-        break;
-    default:
-        Py_UNREACHABLE();
+    Py_ssize_t count = 0;
+    Py_ssize_t n_zeros;
+    int loop_broken = 0;
+    int use_separator = 0; /* First time through, don't append the
+                              separator. They only go between
+                              groups. */
+    Py_ssize_t buffer_pos;
+    Py_ssize_t digits_pos;
+    Py_ssize_t len;
+    Py_ssize_t n_chars;
+    Py_ssize_t remaining = n_digits; /* Number of chars remaining to
+                                        be looked at */
+    /* A generator that returns all of the grouping widths, until it
+       returns 0. */
+    GroupGenerator groupgen;
+    GroupGenerator_init(&groupgen, grouping);
+    const Py_ssize_t thousands_sep_len = PyUnicode_GET_LENGTH(thousands_sep);
+
+    /* if digits are not grouped, thousands separator
+       should be an empty string */
+    assert(!(grouping[0] == CHAR_MAX && thousands_sep_len != 0));
+
+    digits_pos = d_pos + n_digits;
+    if (writer) {
+        buffer_pos = writer->pos + n_buffer;
+        assert(buffer_pos <= PyUnicode_GET_LENGTH(writer->buffer));
+        assert(digits_pos <= PyUnicode_GET_LENGTH(digits));
     }
-    if (unicode != NULL && thousands_sep_kind != kind) {
-        if (thousands_sep_kind < kind)
-            PyMem_Free(thousands_sep_data);
-        else
-            PyMem_Free(data);
+    else {
+        buffer_pos = n_buffer;
     }
-    if (unicode == NULL) {
+
+    if (!writer) {
         *maxchar = 127;
-        if (len != n_digits) {
-            *maxchar = Py_MAX(*maxchar,
-                                   PyUnicode_MAX_CHAR_VALUE(thousands_sep));
+    }
+
+    while ((len = GroupGenerator_next(&groupgen)) > 0) {
+        len = Py_MIN(len, Py_MAX(Py_MAX(remaining, min_width), 1));
+        n_zeros = Py_MAX(0, len - remaining);
+        n_chars = Py_MAX(0, Py_MIN(remaining, len));
+
+        /* Use n_zero zero's and n_chars chars */
+
+        /* Count only, don't do anything. */
+        count += (use_separator ? thousands_sep_len : 0) + n_zeros + n_chars;
+
+        /* Copy into the writer. */
+        InsertThousandsGrouping_fill(writer, &buffer_pos,
+                                     digits, &digits_pos,
+                                     n_chars, n_zeros,
+                                     use_separator ? thousands_sep : NULL,
+                                     thousands_sep_len, maxchar);
+
+        /* Use a separator next time. */
+        use_separator = 1;
+
+        remaining -= n_chars;
+        min_width -= len;
+
+        if (remaining <= 0 && min_width <= 0) {
+            loop_broken = 1;
+            break;
         }
+        min_width -= thousands_sep_len;
+    }
+    if (!loop_broken) {
+        /* We left the loop without using a break statement. */
+
+        len = Py_MAX(Py_MAX(remaining, min_width), 1);
+        n_zeros = Py_MAX(0, len - remaining);
+        n_chars = Py_MAX(0, Py_MIN(remaining, len));
+
+        /* Use n_zero zero's and n_chars chars */
+        count += (use_separator ? thousands_sep_len : 0) + n_zeros + n_chars;
+
+        /* Copy into the writer. */
+        InsertThousandsGrouping_fill(writer, &buffer_pos,
+                                     digits, &digits_pos,
+                                     n_chars, n_zeros,
+                                     use_separator ? thousands_sep : NULL,
+                                     thousands_sep_len, maxchar);
     }
-    return len;
+    return count;
 }
 
 
@@ -9990,36 +10079,12 @@ _PyUnicode_JoinArray(PyObject *separator, PyObject *const *items, Py_ssize_t seq
     return NULL;
 }
 
-#define FILL(kind, data, value, start, length) \
-    do { \
-        Py_ssize_t i_ = 0; \
-        assert(kind != PyUnicode_WCHAR_KIND); \
-        switch ((kind)) { \
-        case PyUnicode_1BYTE_KIND: { \
-            unsigned char * to_ = (unsigned char *)((data)) + (start); \
-            memset(to_, (unsigned char)value, (length)); \
-            break; \
-        } \
-        case PyUnicode_2BYTE_KIND: { \
-            Py_UCS2 * to_ = (Py_UCS2 *)((data)) + (start); \
-            for (; i_ < (length); ++i_, ++to_) *to_ = (value); \
-            break; \
-        } \
-        case PyUnicode_4BYTE_KIND: { \
-            Py_UCS4 * to_ = (Py_UCS4 *)((data)) + (start); \
-            for (; i_ < (length); ++i_, ++to_) *to_ = (value); \
-            break; \
-        } \
-        default: Py_UNREACHABLE(); \
-        } \
-    } while (0)
-
 void
 _PyUnicode_FastFill(PyObject *unicode, Py_ssize_t start, Py_ssize_t length,
                     Py_UCS4 fill_char)
 {
     const enum PyUnicode_Kind kind = PyUnicode_KIND(unicode);
-    const void *data = PyUnicode_DATA(unicode);
+    void *data = PyUnicode_DATA(unicode);
     assert(PyUnicode_IS_READY(unicode));
     assert(unicode_modifiable(unicode));
     assert(fill_char <= PyUnicode_MAX_CHAR_VALUE(unicode));
diff --git a/PC/_findvs.cpp b/PC/_findvs.cpp
deleted file mode 100644 (file)
index ecc68e8..0000000
+++ /dev/null
@@ -1,259 +0,0 @@
-//
-// 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"
-
-#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 _distutils_findvs helper module");
-
-static PyModuleDef_Slot findvs_slots[] = {
-    { Py_mod_exec, exec_findvs },
-    { 0, NULL }
-};
-
-static PyModuleDef findvs_def = {
-    PyModuleDef_HEAD_INIT,
-    "_distutils_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__distutils_findvs(void)
-    {
-        return PyModuleDef_Init(&findvs_def);
-    }
-}
diff --git a/PC/classicAppCompat.can.xml b/PC/classicAppCompat.can.xml
new file mode 100644 (file)
index 0000000..f00475c
--- /dev/null
@@ -0,0 +1 @@
+<CustomCapabilityDescriptor xmlns="http://schemas.microsoft.com/appx/2016/sccd" xmlns:s="http://schemas.microsoft.com/appx/2016/sccd"><CustomCapabilities><CustomCapability Name="Microsoft.classicAppCompat_8wekyb3d8bbwe"></CustomCapability></CustomCapabilities><AuthorizedEntities><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.14_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.15_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.14_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"></AuthorizedEntity><AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.15_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"></AuthorizedEntity></AuthorizedEntities></CustomCapabilityDescriptor>
\ No newline at end of file
diff --git a/PC/classicAppCompat.cat b/PC/classicAppCompat.cat
new file mode 100644 (file)
index 0000000..3d21359
Binary files /dev/null and b/PC/classicAppCompat.cat differ
diff --git a/PC/classicAppCompat.sccd b/PC/classicAppCompat.sccd
new file mode 100644 (file)
index 0000000..9764898
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<CustomCapabilityDescriptor xmlns="http://schemas.microsoft.com/appx/2016/sccd" xmlns:s="http://schemas.microsoft.com/appx/2016/sccd">
+       <CustomCapabilities>
+               <CustomCapability Name="Microsoft.classicAppCompat_8wekyb3d8bbwe"/>
+       </CustomCapabilities>
+       <AuthorizedEntities>
+               <!--PFN for store installation-->
+               <AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"/>
+               <AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"/>
+               <AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"/>
+               <AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"/>
+               <AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"/>
+               <AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"/>
+               <AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"/>
+               <AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.14_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"/>
+               <AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.15_qbz5n2kfra8p0" CertificateSignatureHash="0000000000000000000000000000000000000000000000000000000000000000"/>
+               <AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"/>
+               <AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"/>
+               <AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"/>
+               <AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"/>
+               <AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"/>
+               <AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"/>
+               <AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"/>
+               <AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.14_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"/>
+               <AuthorizedEntity AppPackageFamilyName="PythonSoftwareFoundation.Python.3.15_qbz5n2kfra8p0" CertificateSignatureHash="279cd652c4e252bfbe5217ac722205d7729ba409148cfa9e6d9e5b1cb94eaff1"/>
+       </AuthorizedEntities>
+       <!--Once signed, this file can no longer be modified-->
+       <Catalog>MIIq5AYJKoZIhvcNAQcCoIIq1TCCKtECAQExDzANBglghkgBZQMEAgEFADCCARAGCSsGAQQBgjcKAaCCAQEwgf4wDAYKKwYBBAGCNwwBAQQQaM+L42jwBUGvBczrtolMmhcNMTgxMTMwMDA1OTAzWjAOBgorBgEEAYI3DAEDBQAwgbwwKgQUWKcU3R38DGPlKK33XGIwKtVL1r4xEjAQBgorBgEEAYI3DAIDMQKCADCBjQQg3K+KBOQX7HfxjRNZC9cx8gIPkEhPRO1nJFRdWQrVEJ4xaTAQBgorBgEEAYI3DAIDMQKCADBVBgorBgEEAYI3AgEEMUcwRTAQBgorBgEEAYI3AgEZogKAADAxMA0GCWCGSAFlAwQCAQUABCDcr4oE5Bfsd/GNE1kL1zHyAg+QSE9E7WckVF1ZCtUQnqCCFFAwggZSMIIEOqADAgECAhMzAAMu49KhfNamygpWAAIAAy7jMA0GCSqGSIb3DQEBCwUAMIGMMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMQ0wCwYDVQQLEwRNT1BSMScwJQYDVQQDEx5NaWNyb3NvZnQgTWFya2V0cGxhY2UgQ0EgRyAwMTMwHhcNMTgxMTMwMDA1NTA1WhcNMTgxMjAzMDA1NTA1WjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwpcimfAx3HEpba1GLL/gDaRVddHE5PXTRmwlgaz8kt6/rq5rlrPFnCnbIc5818v0xJIznastbmrq26xyCEHyMLBKnyneTKE36I7+TGjcY0D7ow+o2vY7LDKMCTGlh31fx1Tvrl+5xTbWX5jdLU/3MB5faeOGh+0Knzwx1KDoXWgPtfXnD8I5jxJieoWoCwCjKTJgBOklLy9nbOalxf0h+xQRy2p5fj+PxAwQPgHWft36AF7/IMbt9FcXMtg4xdpnTYz4OV3dFOPz4m3M8HwVgNMv89W/1Ozc7uOyZt0Ij1baT6r2L3IjYg5ftzpGqaDOFcWlyDFSdhMR6BIKW8xEpAgMBAAGjggHCMIIBvjAYBgNVHSUBAf8EDjAMBgorBgEEAYI3TBwBMB0GA1UdDgQWBBRdpGYiCytx83FYzPSl+o97YzpxGzAPBgNVHREECDAGggRNT1BSMB8GA1UdIwQYMBaAFEnYB1RFhpclHtZZcRLDcpt0OE3oMGIGA1UdHwRbMFkwV6BVoFOGUWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyME1hcmtldHBsYWNlJTIwQ0ElMjBHJTIwMDEzKDIpLmNybDBvBggrBgEFBQcBAQRjMGEwXwYIKwYBBQUHMAKGU2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwTWFya2V0cGxhY2UlMjBDQSUyMEclMjAwMTMoMikuY3J0MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgXgMDwGCSsGAQQBgjcVBwQvMC0GJSsGAQQBgjcVCIOS9kTqrxCDkY0wgqzgLIKinDE0g+6NOIaE7wACAWQCARYwIAYJKwYBBAGCNxUKAQH/BBAwDjAMBgorBgEEAYI3TBwBMA0GCSqGSIb3DQEBCwUAA4ICAQB3Dk3rXH52CDq/z1fwqn9xI5WGjGmu6oAE4HSc3sNdFrSVMMGm4gTlYGWSZ0wJUUf16mVr/rdXhxuR3MZn+m4Bhdl8KQqYjYbIvCUVj0o9nZ+yT6foeY8bKnB+K5h6rol+mjDj5IfcutC4x2Kx5RrtDtRTSoKA63iZ74DYngPpBGBBgaS2c/QzgqPRAMMRqy2KBDP0miCnpR3F4YlzHGyOZwyHhESjYd9kwF47+msuHS04JZpnGHIvBppKN9XQzH3WezNnnX3lz4AyAUMsMFuARqEnacUhrAHL9n5zMv9CzxDYN1r1/aDh/788RuGuZM+E3NtmbxJJ7j6T5/VtXNBRgKtIq8d2+11j6qvKLigOTxSC25/A70BZBEvllLFnvc1vA2LrC9drwt1KpSmWie1nvpilw7o+gHMOG9utUxGha2VuVizuVNGCywTRRjvmGS1QqTfaun1URVrLfnDINXuTgN1Vwp0J5IGpJ3D8yj01NDQ/RworE+3W/R531NBYova9QRhU/igEw/Aa/q8wjZ4Pzxr9oBIo0Ta3Tv6qIggaWXw0U9+F0J7SCqIhn0d0ATO+E1Qs/SxZIAICLwmqzoLYUAh8q153esBs4uesueqgt5ueyHK8V3WjMS4wxEyVN5ZMET3hFtEshsZC31tLDdjq750U4SgQVmoYSm3F3ZOKQDCCBtcwggS/oAMCAQICCmESRKIAAAAAAAIwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDExMB4XDTExMDMyODIxMDkzOVoXDTMxMDMyODIxMTkzOVowfTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEnMCUGA1UEAxMeTWljcm9zb2Z0IE1hcmtldFBsYWNlIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAubUaSwGYVsE3MAnPfvmozUhAB3qxBABgJRW1vDp4+tVinXxD32f7k1K89JQ6zDOgS/iDgULC+yFK1K/1Qjac/0M7P6c8v5LSjnWGlERLa/qY32j46S7SLQcit3g2jgoTTO03eUG+9yHZUTGV/FJdRYB8uXhrznJBa+Y+yGwiQKF+m6XFeBH/KORoKFx+dmMoy9EWJ/m/o9IiUj2kzm9C691+vZ/I2w0Bj93W9SPPkV2PCNHlzgfIAoeajWpHmi38Wi3xZHonkzAVBHxPsCBppOoNsWvmAfUM7eBthkSPvFruekyDCPNEYhfGqgqtqLkoBebXLZCOVybF7wTQaLvse60//3P003icRcCoQYgY4NAqrF7j80o5U7DkeXxcB0xvengsaKgiAaV1DKkRbpe98wCqr1AASvm5rAJUYMU+mXmOieV2EelY2jGrenWe9FQpNXYV1NoWBh0WKoFxttoWYAnF705bIWtSZsz08ZfK6WLX4GXNLcPBlgCzfTm1sdKYASWdBbH2haaNhPapFhQQBJHKwnVW2iXErImhuPi45W3MVTZ5D9ASshZx69cLYY6xAdIa+89Kf/uRrsGOVZfahDuDw+NI183iAyzC8z/QRt2P32LYxP0xrCdqVh+DJo2i4NoE8Uk1usCdbVRuBMBQl/AwpOTq7IMvHGElf65CqzUCAwEAAaOCAUswggFHMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBQPU8s/FmEl/mCJHdO5fOiQrbOU0TAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCjuZmM8ZVNDgp9wHsL4RY8KJ8nLinvxFTphNGCrxaLknkYG5pmMhVlX+UB/tSiW8W13W60nggz9u5xwMx7v/1t/Tgm6g2brVyOKI5A7u6/2SIJwkJKFw953K0YIKVT28w9zl8dSJnmRnyR0G86ncWbF6CLQ6A6lBQ9o2mTGVqDr4m35WKAnc6YxUUM1y74mbzFFZr63VHsCcOp3pXWnUqAY1rb6Q6NX1b3clncKqLFm0EjKHcQ56grTbwuuB7pMdh/IFCJR01MQzQbDtpEisbOeZUi43YVAAHKqI1EO9bRwg3frCjwAbml9MmI4utMW94gWFgvrMxIX+n42RBDIjf3Ot3jkT6gt3XeTTmO9bptgblZimhERdkFRUFpVtkocJeLoGuuzP93uH/Yp032wzRH+XmMgujfZv+vnfllJqxdowoQLx55FxLLeTeYfwi/xMSjZO2gNven3U/3KeSCd1kUOFS3AOrwZ0UNOXJeW5JQC6Vfd1BavFZ6FAta1fMLu3WFvNB+FqeHUaU3ya7rmtxJnzk29DeSqXgGNmVSywBS4NajI5jJIKAA6UhNJlsg8CHYwUOKf5ej8OoQCkbadUxXygAfxCfW2YBbujtI+PoyejRFxWUjYFWO5LeTI62UMyqfOEiqugoYjNxmQZla2s4YHVuqIC34R85FQlg9pKQBsDCCBxswggUDoAMCAQICEzMAAABCs21EHGjyqKYAAAAAAEIwDQYJKoZIhvcNAQELBQAwfTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEnMCUGA1UEAxMeTWljcm9zb2Z0IE1hcmtldFBsYWNlIFBDQSAyMDExMB4XDTE4MDQyMDE2NDI0NFoXDTIxMDQyMDE2NDI0NFowgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIxJzAlBgNVBAMTHk1pY3Jvc29mdCBNYXJrZXRwbGFjZSBDQSBHIDAxMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOZ2KM9Pq1YCOiqWOivmHjUtkMgznTMP/Mr2YfzZeIIJySg1F4WxFZc4jagGHHNof9NRT+GGnktWsXkZuH1DzQEG4Ps1ln8+4vhbDglqu5ymDnd6RmsyoD+8xfc8bBIvE5o6R+ES4/GVD5TqNsOrWbwETaIZVbmTulJLoTS1WSsSjowmbc+sHqZiY8BNJNThUEmXSjuHqkQKKshuiFWYEqOTitp71mBLyH1wN7/jThRzGpolOeFusRNJdb8sEqvNzEN9Qh+Kp6ndzrnjE+t8ixXW3lShyyOOZqQMwsQn9q9T0v7Q69GuojBTFBOHKwigcCHr4xahuN+ZYMk0xGg+sm3Uj7I9mrWTSTiIRMZNIWq3sFg4+rFg48NYfRlXUpONmL7vXq6v1pIU99d2MXQ6uUrnUr1/n5ZiHGCeFcvWwqO8BYHdcTlrSOkayfFp7W9oCk9QO4Xy0h9cQRedRo2kvdTHxIuJS70Hdv6oePPF2ZFaLucUzzwsR4/XMAVKY8Vsm950omsSSOImsMtzavUdQM+wZFxvHTRqVDkF3quPdME0bCZOWB4hQJmd+o2clw+1mpwPu0/M92nA9FJg7MGPxkFaYW7g26jSqUJZ9AcX+Xa5TSIeqMZt3cRVjMTx0T/v73Sv8TpalqIQ5Fde1+hFK07sOAm3TwgzvlVJnbYgp0/rAgMBAAGjggGCMIIBfjASBgkrBgEEAYI3FQEEBQIDAgACMCMGCSsGAQQBgjcVAgQWBBSbJnDhuc3nQXuKuACsPflEbwjbozAdBgNVHQ4EFgQUSdgHVEWGlyUe1llxEsNym3Q4TegwEQYDVR0gBAowCDAGBgRVHSAAMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMB8GA1UdIwQYMBaAFA9Tyz8WYSX+YIkd07l86JCts5TRMFcGA1UdHwRQME4wTKBKoEiGRmh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY01hclBDQTIwMTFfMjAxMS0wMy0yOC5jcmwwWwYIKwYBBQUHAQEETzBNMEsGCCsGAQUFBzAChj9odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY01hclBDQTIwMTFfMjAxMS0wMy0yOC5jcnQwDQYJKoZIhvcNAQELBQADggIBAIa2oa6kvuIHCNfz7anlL0W9tOCt8gQNkxOGRK3yliQIelNQahDJojyEFlHQ2BcHL5oZit3WeSDoYddhojx6YzJIWwfGwtVqgc0JFDKJJ2ZXRYMRsuy01Hn25xob+zRMS6VmV1axQn6uwOSMcgYmzoroh6edjPKu7qXcpt6LmhF2qFvLySA7wBCwfI/rR5/PX6I7a07Av7PpbY6/+2ujd8m1H3hwMrb4Hq3z6gcq62zJ3nDXUbC0Bp6Jt2kV9f0rEFpDK9oxE2qrGBUf8c3O2XirHOgAjRyWjWWtVms+MP8qBIA1NSLrBmToEWVP3sEkQZWMkoZWo4rYEJZpX7UIgdDc9zYNakgTCJqPhqn8AE1sgSSnpqAdMkkP41rTlFCv2ig2QVzDerjGfEv+uPDnlAT0kucbBJxHHvUC4aqUxaTSa0sy2bZ6NWFx8/u0gW8JahzxYvvvZL8SfwaA9P4ETb8pH1jw+6N/LfM2zJrNKhf5hjKa0VDOXUpkYq60OqVVnWJ6oJaSIWNkZKfzPnl/UHA8Bh4qfVrhc9H5PExPhhB9WVTsjf4r+OOVuolJldThcWQqljiPjk5rultr63G5xLyFpxNi4BCrcNQBJFB5wKgOWOyjQTVWTmh2ESaeqZ2aWBjftFHlxJ/qYc7WOGJV0+cHGkB/dvFxmKnv6tuWexiMMYIVUTCCFU0CAQEwgaQwgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIxJzAlBgNVBAMTHk1pY3Jvc29mdCBNYXJrZXRwbGFjZSBDQSBHIDAxMwITMwADLuPSoXzWpsoKVgACAAMu4zANBglghkgBZQMEAgEFAKCBlTAYBgkqhkiG9w0BCQMxCwYJKwYBBAGCNwoBMC8GCSqGSIb3DQEJBDEiBCAS0d3bw2YOODvKFr0S4e3BDnaDcZXUKeBO77yvkWzVojBIBgorBgEEAYI3AgEMMTowOKAegBwATQBpAGMAcgBvAHMAbwBmAHQAIABDAG8AcgBwoRaAFGh0dHA6Ly9NaWNyb3NvZnQuY29tMA0GCSqGSIb3DQEBAQUABIIBABoap3Y+2k+zFz2cCmkc8xxHnpIygLsUSRMXeXdjPVcYx3o5cPLIixnL6p8+LIrlIagPg23mzTEmnjZaO4aaexk+3XojlHj22w/bEigEDnKyWt5bHeS0UNHJbxEFYRfd84IP1+mSH4c4+GuU9p3LsAMh6wN03MYrGmczUOnlP6YlxHNQbQxnV0sl14yOE5ni9oT4y+l+SllvbV3/Jhwpov68aoP/2MazqxR4QyGfSxhCPJ4UuDHU7IrpnTxGBTL1/oUU8ED0FxyDoH/Sc5OhTLInFqbZaVzm5Mpr12wYUBL4nE5h0Kf6BCKdgM8a+Ti3wMUsBoC79ff3jE9U/xwSneOhghLlMIIS4QYKKwYBBAGCNwMDATGCEtEwghLNBgkqhkiG9w0BBwKgghK+MIISugIBAzEPMA0GCWCGSAFlAwQCAQUAMIIBUQYLKoZIhvcNAQkQAQSgggFABIIBPDCCATgCAQEGCisGAQQBhFkKAwEwMTANBglghkgBZQMEAgEFAAQghPy22lwuCYESw8jYhb4F9ZDPJ1LPgSSZgJDkyXYzVt4CBlv98KtAoBgTMjAxODExMzAwMTA1MTkuMTM4WjAEgAIB9KCB0KSBzTCByjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046RDA4Mi00QkZELUVFQkExJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIHNlcnZpY2Wggg48MIIE8TCCA9mgAwIBAgITMwAAAOIYOHtm6erB2AAAAAAA4jANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0xODA4MjMyMDI3MDNaFw0xOTExMjMyMDI3MDNaMIHKMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpEMDgyLTRCRkQtRUVCQTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgc2VydmljZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKirA72FF3NCLW5mfLO/D0EZ5Ycs00oiMSissXLB6WF9GNdP78QzFwAypxW/+qZSczqaHbDH8hlbxkzf3DiYgAdpQjnGkLujwKtWSaP29/lVf7jFqHy9v6eH+LdOi0LvtrPRW34MyCvpxZyOW4H1h3PkxCBL5Ra21sDqgcVL1me0osw8QTURXmI4LyeLdTH3CcI2AgNDXTjsFBf3QsO+JYyAOYWrTcLnywVN6DrigmgrDJk5w+wR4VrHfl2T9PRZbZ+UDt13wwyB9d6IURuzV8lHsAVfF8t9S0aGVPmkQ3c2waOhHpsp6VEM+T5D2Ph8xJX1r82z67WRlmGcOP2NWC0CAwEAAaOCARswggEXMB0GA1UdDgQWBBSJPpD6BsP2p+crDJL232voEtLxezAfBgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNUaW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQARQHu7ISeBuJSHKuDRI04704cH0B7BYzeEIrD15awviMRcYIfIOHpvGzZOWQgP2Hm0Rr7kvTUu1VrSSaQ7i1gPWdhqMmw5WBnSS5bxeMhhx9UsASeE84vUu82NeZapGSjH38YAb4WT+TtiTkcoI59rA+CTCq108ttIxVfZcr3id76OETIH0HvhlnxOOWjwGy4ul6Za5RoTLG/oo2rrGmVi3FwrNWGezYLBODuEsjzG36lCRtBKC2ZAHfbOz5wtkUHbqh79mUKocjP4r3qxf5TN87yf6g1uTx+J8pdnAi5iHt+ZtangWqnVTE8PoIREWhBVlGFfQdkELUx2Or90aAqWMIIGcTCCBFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcNMjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEwRA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQedGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKxXf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4GkbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEAAaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0gAQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOhIW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS+7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlKkVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon/VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOiPPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCIIYdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7aKLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQcdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+NR4Iuto229Nfj950iEkSoYICzjCCAjcCAQEwgfihgdCkgc0wgcoxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJXQTEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOkQwODItNEJGRC1FRUJBMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBzZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQByQCUheEOevaI9Zc/3QGrkX42iC6CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBBQUAAgUA36ppYDAiGA8yMDE4MTEyOTIxMzQyNFoYDzIwMTgxMTMwMjEzNDI0WjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDfqmlgAgEAMAoCAQACAitfAgH/MAcCAQACAhGtMAoCBQDfq7rgAgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEAbAXXPR9wy4NA0892GGqetaZF+pNClpGcfEpSuHABaZ4Gzr1nY1nmrhexTtr/U6omHALRWzkQwthk0cy+mnEHXyOZGmoEEpgrLgK3AAP5NbK/XbtHQRyZJQyhZScFbOyQycoE8QQalSVOhWxk/bbBMQaQiYVMIexNd/T0KgaDDUMxggMNMIIDCQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAOIYOHtm6erB2AAAAAAA4jANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCCr9IiSbx6s8MLdxldRG49+4h6CbicW8hWXAicI3jNmhDCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIN8BpJSmQCGubWwVa4tW+aMveoHMX/nDnVN8fiDOMsrLMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAADiGDh7ZunqwdgAAAAAAOIwIgQgTkOfRvGEZNbr5/hgWclsL4/Q7SOZihE/U0lz2wEMIGcwDQYJKoZIhvcNAQELBQAEggEATlxnCfTzFfTMDvK085zlYPVCroKYW6gKFYnbAhNmrNzcxqALKmIYXpFU7B6HH/vYzkUfCyXpf5tsyEWu0oTySOjyAZ9+2vdaG8nEgjOp0L737lcitgusIjpWtta3Ik0b+mzffnvyjrgTSuKDDni3mxGfvJU77k1Ctempma4H2FJso6Bur0PRH99vIYDu4lHigOSLbeyjR5CiDciBwEVUSA0FxhoFNX1yfpxz3sukOvkaoTduREIjH5LxUjNI1ZTMK/ZkeETI8IPRpWVzAc8q7CujErHKo4sdKej/O2cfUTUHplFLVCGGExpJUCg5FH5jVUUFt75ad8503sdGplggVQ==</Catalog></CustomCapabilityDescriptor>
diff --git a/PC/external/Externals.txt b/PC/external/Externals.txt
deleted file mode 100644 (file)
index 618fe16..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-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
deleted file mode 100644 (file)
index 1fb3187..0000000
+++ /dev/null
@@ -1,827 +0,0 @@
-// 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
deleted file mode 100644 (file)
index 675a501..0000000
Binary files a/PC/external/v140/amd64/Microsoft.VisualStudio.Setup.Configuration.Native.lib and /dev/null 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
deleted file mode 100644 (file)
index 40f70b2..0000000
Binary files a/PC/external/v140/win32/Microsoft.VisualStudio.Setup.Configuration.Native.lib and /dev/null 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
deleted file mode 100644 (file)
index 675a501..0000000
Binary files a/PC/external/v141/amd64/Microsoft.VisualStudio.Setup.Configuration.Native.lib and /dev/null 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
deleted file mode 100644 (file)
index 40f70b2..0000000
Binary files a/PC/external/v141/win32/Microsoft.VisualStudio.Setup.Configuration.Native.lib and /dev/null differ
index bc85b58..1b553d5 100644 (file)
@@ -536,10 +536,16 @@ static _PyInitError
 get_program_full_path(const _PyCoreConfig *core_config,
                       PyCalculatePath *calculate, _PyPathConfig *config)
 {
+    const wchar_t *pyvenv_launcher;
     wchar_t program_full_path[MAXPATHLEN+1];
     memset(program_full_path, 0, sizeof(program_full_path));
 
-    if (!GetModuleFileNameW(NULL, program_full_path, MAXPATHLEN)) {
+    /* The launcher may need to force the executable path to a
+     * different environment, so override it here. */
+    pyvenv_launcher = _wgetenv(L"__PYVENV_LAUNCHER__");
+    if (pyvenv_launcher && pyvenv_launcher[0]) {
+        wcscpy_s(program_full_path, MAXPATHLEN+1, pyvenv_launcher);
+    } else if (!GetModuleFileNameW(NULL, program_full_path, MAXPATHLEN)) {
         /* GetModuleFileName should never fail when passed NULL */
         return _Py_INIT_ERR("Cannot determine program path");
     }
@@ -570,6 +576,9 @@ read_pth_file(_PyPathConfig *config, wchar_t *prefix, const wchar_t *path,
     size_t prefixlen = wcslen(prefix);
 
     wchar_t *buf = (wchar_t*)PyMem_RawMalloc(bufsiz * sizeof(wchar_t));
+    if (buf == NULL) {
+        goto error;
+    }
     buf[0] = '\0';
 
     while (!feof(sp_file)) {
@@ -597,17 +606,22 @@ read_pth_file(_PyPathConfig *config, wchar_t *prefix, const wchar_t *path,
 
         DWORD wn = MultiByteToWideChar(CP_UTF8, 0, line, -1, NULL, 0);
         wchar_t *wline = (wchar_t*)PyMem_RawMalloc((wn + 1) * sizeof(wchar_t));
+        if (wline == NULL) {
+            goto error;
+        }
         wn = MultiByteToWideChar(CP_UTF8, 0, line, -1, wline, wn + 1);
         wline[wn] = '\0';
 
         size_t usedsiz = wcslen(buf);
         while (usedsiz + wn + prefixlen + 4 > bufsiz) {
             bufsiz += MAXPATHLEN;
-            buf = (wchar_t*)PyMem_RawRealloc(buf, (bufsiz + 1) * sizeof(wchar_t));
-            if (!buf) {
+            wchar_t *tmp = (wchar_t*)PyMem_RawRealloc(buf, (bufsiz + 1) *
+                                                            sizeof(wchar_t));
+            if (tmp == NULL) {
                 PyMem_RawFree(wline);
                 goto error;
             }
+            buf = tmp;
         }
 
         if (usedsiz) {
diff --git a/PC/icons/pythonwx150.png b/PC/icons/pythonwx150.png
new file mode 100644 (file)
index 0000000..4c3eb31
Binary files /dev/null and b/PC/icons/pythonwx150.png differ
diff --git a/PC/icons/pythonwx44.png b/PC/icons/pythonwx44.png
new file mode 100644 (file)
index 0000000..e3b32a8
Binary files /dev/null and b/PC/icons/pythonwx44.png differ
diff --git a/PC/icons/pythonx150.png b/PC/icons/pythonx150.png
new file mode 100644 (file)
index 0000000..5f8d304
Binary files /dev/null and b/PC/icons/pythonx150.png differ
diff --git a/PC/icons/pythonx44.png b/PC/icons/pythonx44.png
new file mode 100644 (file)
index 0000000..3881daa
Binary files /dev/null and b/PC/icons/pythonx44.png differ
diff --git a/PC/icons/pythonx50.png b/PC/icons/pythonx50.png
new file mode 100644 (file)
index 0000000..7cc3aec
Binary files /dev/null and b/PC/icons/pythonx50.png differ
index eb3433a..4c620da 100644 (file)
@@ -28,7 +28,7 @@
 #define RC_NO_PYTHON        103
 #define RC_NO_MEMORY        104
 /*
- * SCRIPT_WRAPPER is used to choose between two variants of an executable built
+ * SCRIPT_WRAPPER is used to choose one of the variants of an executable built
  * from this source file. If not defined, the PEP 397 Python launcher is built;
  * if defined, a script launcher of the type used by setuptools is built, which
  * looks for a script name related to the executable name and runs that script
 #if defined(SCRIPT_WRAPPER)
 #define RC_NO_SCRIPT        105
 #endif
+/*
+ * VENV_REDIRECT is used to choose the variant that looks for an adjacent or
+ * one-level-higher pyvenv.cfg, and uses its "home" property to locate and
+ * launch the original python.exe.
+ */
+#if defined(VENV_REDIRECT)
+#define RC_NO_VENV_CFG      106
+#define RC_BAD_VENV_CFG     107
+#endif
 
 /* Just for now - static definition */
 
@@ -97,7 +106,7 @@ error(int rc, wchar_t * format, ... )
 #if !defined(_WINDOWS)
     fwprintf(stderr, L"%ls\n", message);
 #else
-    MessageBox(NULL, message, TEXT("Python Launcher is sorry to say ..."),
+    MessageBoxW(NULL, message, L"Python Launcher is sorry to say ...",
                MB_OK);
 #endif
     exit(rc);
@@ -131,6 +140,17 @@ static wchar_t * get_env(wchar_t * key)
     return buf;
 }
 
+#if defined(_DEBUG)
+#if defined(_WINDOWS)
+
+#define PYTHON_EXECUTABLE L"pythonw_d.exe"
+
+#else
+
+#define PYTHON_EXECUTABLE L"python_d.exe"
+
+#endif
+#else
 #if defined(_WINDOWS)
 
 #define PYTHON_EXECUTABLE L"pythonw.exe"
@@ -140,6 +160,7 @@ static wchar_t * get_env(wchar_t * key)
 #define PYTHON_EXECUTABLE L"python.exe"
 
 #endif
+#endif
 
 #define MAX_VERSION_SIZE    4
 
@@ -1118,6 +1139,7 @@ static PYC_MAGIC magic_values[] = {
     { 3320, 3351, L"3.5" },
     { 3360, 3379, L"3.6" },
     { 3390, 3399, L"3.7" },
+    { 3400, 3409, L"3.8" },
     { 0 }
 };
 
@@ -1453,7 +1475,88 @@ show_python_list(wchar_t ** argv)
         fwprintf(stderr, L"\n\nCan't find a Default Python.\n\n");
     else
         fwprintf(stderr, L"\n\n"); /* End with a blank line */
-    return(FALSE); /* If this has been called we cannot continue */
+    return FALSE; /* If this has been called we cannot continue */
+}
+
+#if defined(VENV_REDIRECT)
+
+static int
+find_home_value(const char *buffer, const char **start, DWORD *length)
+{
+    for (const char *s = strstr(buffer, "home"); s; s = strstr(s + 1, "\nhome")) {
+        if (*s == '\n') {
+            ++s;
+        }
+        for (int i = 4; i > 0 && *s; --i, ++s);
+
+        while (*s && iswspace(*s)) {
+            ++s;
+        }
+        if (*s != L'=') {
+            continue;
+        }
+
+        do {
+            ++s;
+        } while (*s && iswspace(*s));
+
+        *start = s;
+        char *nl = strchr(s, '\n');
+        if (nl) {
+            *length = (DWORD)((ptrdiff_t)nl - (ptrdiff_t)s);
+        } else {
+            *length = (DWORD)strlen(s);
+        }
+        return 1;
+    }
+    return 0;
+}
+#endif
+
+static wchar_t *
+wcsdup_pad(const wchar_t *s, int padding, int *newlen)
+{
+    size_t len = wcslen(s);
+    len += 1 + padding;
+    wchar_t *r = (wchar_t *)malloc(len * sizeof(wchar_t));
+    if (!r) {
+        return NULL;
+    }
+    if (wcscpy_s(r, len, s)) {
+        free(r);
+        return NULL;
+    }
+    *newlen = len < MAXINT ? (int)len : MAXINT;
+    return r;
+}
+
+static wchar_t *
+get_process_name()
+{
+    DWORD bufferLen = MAX_PATH;
+    DWORD len = bufferLen;
+    wchar_t *r = NULL;
+
+    while (!r) {
+        r = (wchar_t *)malloc(bufferLen * sizeof(wchar_t));
+        if (!r) {
+            error(RC_NO_MEMORY, L"out of memory");
+            return NULL;
+        }
+        len = GetModuleFileNameW(NULL, r, bufferLen);
+        if (len == 0) {
+            free(r);
+            error(0, L"Failed to get module name");
+            return NULL;
+        } else if (len == bufferLen &&
+                   GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+            free(r);
+            r = NULL;
+            bufferLen *= 2;
+        }
+    }
+
+    return r;
 }
 
 static int
@@ -1463,21 +1566,27 @@ process(int argc, wchar_t ** argv)
     wchar_t * command;
     wchar_t * executable;
     wchar_t * p;
+    wchar_t * argv0;
     int rc = 0;
-    size_t plen;
     INSTALLED_PYTHON * ip;
     BOOL valid;
     DWORD size, attrs;
-    HRESULT hr;
     wchar_t message[MSGSIZE];
     void * version_data;
     VS_FIXEDFILEINFO * file_info;
     UINT block_size;
-    int index;
-#if defined(SCRIPT_WRAPPER)
+#if defined(VENV_REDIRECT)
+    wchar_t * venv_cfg_path;
     int newlen;
+#elif defined(SCRIPT_WRAPPER)
     wchar_t * newcommand;
     wchar_t * av[2];
+    int newlen;
+    HRESULT hr;
+    int index;
+#else
+    HRESULT hr;
+    int index;
 #endif
 
     setvbuf(stderr, (char *)NULL, _IONBF, 0);
@@ -1495,6 +1604,7 @@ process(int argc, wchar_t ** argv)
 #else
     debug(L"launcher executable: Console\n");
 #endif
+#if !defined(VENV_REDIRECT)
     /* Get the local appdata folder (non-roaming) */
     hr = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA,
                           NULL, 0, appdata_ini_path);
@@ -1503,9 +1613,7 @@ process(int argc, wchar_t ** argv)
         appdata_ini_path[0] = L'\0';
     }
     else {
-        plen = wcslen(appdata_ini_path);
-        p = &appdata_ini_path[plen];
-        wcsncpy_s(p, MAX_PATH - plen, L"\\py.ini", _TRUNCATE);
+        wcsncat_s(appdata_ini_path, MAX_PATH, L"\\py.ini", _TRUNCATE);
         attrs = GetFileAttributesW(appdata_ini_path);
         if (attrs == INVALID_FILE_ATTRIBUTES) {
             debug(L"File '%ls' non-existent\n", appdata_ini_path);
@@ -1514,8 +1622,9 @@ process(int argc, wchar_t ** argv)
             debug(L"Using local configuration file '%ls'\n", appdata_ini_path);
         }
     }
-    plen = GetModuleFileNameW(NULL, launcher_ini_path, MAX_PATH);
-    size = GetFileVersionInfoSizeW(launcher_ini_path, &size);
+#endif
+    argv0 = get_process_name();
+    size = GetFileVersionInfoSizeW(argv0, &size);
     if (size == 0) {
         winerror(GetLastError(), message, MSGSIZE);
         debug(L"GetFileVersionInfoSize failed: %ls\n", message);
@@ -1523,7 +1632,7 @@ process(int argc, wchar_t ** argv)
     else {
         version_data = malloc(size);
         if (version_data) {
-            valid = GetFileVersionInfoW(launcher_ini_path, 0, size,
+            valid = GetFileVersionInfoW(argv0, 0, size,
                                         version_data);
             if (!valid)
                 debug(L"GetFileVersionInfo failed: %X\n", GetLastError());
@@ -1540,15 +1649,51 @@ process(int argc, wchar_t ** argv)
             free(version_data);
         }
     }
+
+#if defined(VENV_REDIRECT)
+    /* Allocate some extra space for new filenames */
+    venv_cfg_path = wcsdup_pad(argv0, 32, &newlen);
+    if (!venv_cfg_path) {
+        error(RC_NO_MEMORY, L"Failed to copy module name");
+    }
+    p = wcsrchr(venv_cfg_path, L'\\');
+
+    if (p == NULL) {
+        error(RC_NO_VENV_CFG, L"No pyvenv.cfg file");
+    }
+    p[0] = L'\0';
+    wcscat_s(venv_cfg_path, newlen, L"\\pyvenv.cfg");
+    attrs = GetFileAttributesW(venv_cfg_path);
+    if (attrs == INVALID_FILE_ATTRIBUTES) {
+        debug(L"File '%ls' non-existent\n", venv_cfg_path);
+        p[0] = '\0';
+        p = wcsrchr(venv_cfg_path, L'\\');
+        if (p != NULL) {
+            p[0] = '\0';
+            wcscat_s(venv_cfg_path, newlen, L"\\pyvenv.cfg");
+            attrs = GetFileAttributesW(venv_cfg_path);
+            if (attrs == INVALID_FILE_ATTRIBUTES) {
+                debug(L"File '%ls' non-existent\n", venv_cfg_path);
+                error(RC_NO_VENV_CFG, L"No pyvenv.cfg file");
+            }
+        }
+    }
+    debug(L"Using venv configuration file '%ls'\n", venv_cfg_path);
+#else
+    /* Allocate some extra space for new filenames */
+    if (wcscpy_s(launcher_ini_path, MAX_PATH, argv0)) {
+        error(RC_NO_MEMORY, L"Failed to copy module name");
+    }
     p = wcsrchr(launcher_ini_path, L'\\');
+
     if (p == NULL) {
         debug(L"GetModuleFileNameW returned value has no backslash: %ls\n",
               launcher_ini_path);
         launcher_ini_path[0] = L'\0';
     }
     else {
-        wcsncpy_s(p, MAX_PATH - (p - launcher_ini_path), L"\\py.ini",
-                  _TRUNCATE);
+        p[0] = L'\0';
+        wcscat_s(launcher_ini_path, MAX_PATH, L"\\py.ini");
         attrs = GetFileAttributesW(launcher_ini_path);
         if (attrs == INVALID_FILE_ATTRIBUTES) {
             debug(L"File '%ls' non-existent\n", launcher_ini_path);
@@ -1557,6 +1702,7 @@ process(int argc, wchar_t ** argv)
             debug(L"Using global configuration file '%ls'\n", launcher_ini_path);
         }
     }
+#endif
 
     command = skip_me(GetCommandLineW());
     debug(L"Called with command line: %ls\n", command);
@@ -1592,6 +1738,55 @@ process(int argc, wchar_t ** argv)
         command = newcommand;
         valid = FALSE;
     }
+#elif defined(VENV_REDIRECT)
+    {
+        FILE *f;
+        char buffer[4096]; /* 4KB should be enough for anybody */
+        char *start;
+        DWORD len, cch, cch_actual;
+        size_t cb;
+        if (_wfopen_s(&f, venv_cfg_path, L"r")) {
+            error(RC_BAD_VENV_CFG, L"Cannot read '%ls'", venv_cfg_path);
+        }
+        cb = fread_s(buffer, sizeof(buffer), sizeof(buffer[0]),
+                     sizeof(buffer) / sizeof(buffer[0]), f);
+        fclose(f);
+
+        if (!find_home_value(buffer, &start, &len)) {
+            error(RC_BAD_VENV_CFG, L"Cannot find home in '%ls'",
+                  venv_cfg_path);
+        }
+
+        cch = MultiByteToWideChar(CP_UTF8, 0, start, len, NULL, 0);
+        if (!cch) {
+            error(0, L"Cannot determine memory for home path");
+        }
+        cch += (DWORD)wcslen(PYTHON_EXECUTABLE) + 1 + 1; /* include sep and null */
+        executable = (wchar_t *)malloc(cch * sizeof(wchar_t));
+        if (executable == NULL) {
+            error(RC_NO_MEMORY, L"A memory allocation failed");
+        }
+        cch_actual = MultiByteToWideChar(CP_UTF8, 0, start, len, executable, cch);
+        if (!cch_actual) {
+            error(RC_BAD_VENV_CFG, L"Cannot decode home path in '%ls'",
+                  venv_cfg_path);
+        }
+        if (executable[cch_actual - 1] != L'\\') {
+            executable[cch_actual++] = L'\\';
+            executable[cch_actual] = L'\0';
+        }
+        if (wcscat_s(executable, cch, PYTHON_EXECUTABLE)) {
+            error(RC_BAD_VENV_CFG, L"Cannot create executable path from '%ls'",
+                  venv_cfg_path);
+        }
+        if (GetFileAttributesW(executable) == INVALID_FILE_ATTRIBUTES) {
+            error(RC_NO_PYTHON, L"No Python at '%ls'", executable);
+        }
+        if (!SetEnvironmentVariableW(L"__PYVENV_LAUNCHER__", argv0)) {
+            error(0, L"Failed to set launcher environment");
+        }
+        valid = 1;
+    }
 #else
     if (argc <= 1) {
         valid = FALSE;
@@ -1599,12 +1794,12 @@ process(int argc, wchar_t ** argv)
     }
     else {
         p = argv[1];
-        plen = wcslen(p);
-        if ((argc == 2) && 
-            (!wcsncmp(p, L"-0", wcslen(L"-0")) || /* Starts with -0 or --list */
+        if ((argc == 2) && // list version args
+            (!wcsncmp(p, L"-0", wcslen(L"-0")) ||
             !wcsncmp(p, L"--list", wcslen(L"--list"))))
         {
-            valid = show_python_list(argv); /* Check for -0 or --list FIRST */
+            show_python_list(argv);
+            return rc;
         }
         valid = valid && (*p == L'-') && validate_version(&p[1]);
         if (valid) {
diff --git a/PC/layout/__init__.py b/PC/layout/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/PC/layout/__main__.py b/PC/layout/__main__.py
new file mode 100644 (file)
index 0000000..f7aa1e6
--- /dev/null
@@ -0,0 +1,14 @@
+import sys
+
+try:
+    import layout
+except ImportError:
+    # Failed to import our package, which likely means we were started directly
+    # Add the additional search path needed to locate our module.
+    from pathlib import Path
+
+    sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
+
+from layout.main import main
+
+sys.exit(int(main() or 0))
diff --git a/PC/layout/main.py b/PC/layout/main.py
new file mode 100644 (file)
index 0000000..217b2b0
--- /dev/null
@@ -0,0 +1,616 @@
+"""
+Generates a layout of Python for Windows from a build.
+
+See python make_layout.py --help for usage.
+"""
+
+__author__ = "Steve Dower <steve.dower@python.org>"
+__version__ = "3.8"
+
+import argparse
+import functools
+import os
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+import zipfile
+
+from pathlib import Path
+
+if __name__ == "__main__":
+    # Started directly, so enable relative imports
+    __path__ = [str(Path(__file__).resolve().parent)]
+
+from .support.appxmanifest import *
+from .support.catalog import *
+from .support.constants import *
+from .support.filesets import *
+from .support.logging import *
+from .support.options import *
+from .support.pip import *
+from .support.props import *
+
+BDIST_WININST_FILES_ONLY = FileNameSet("wininst-*", "bdist_wininst.py")
+BDIST_WININST_STUB = "PC/layout/support/distutils.command.bdist_wininst.py"
+
+TEST_PYDS_ONLY = FileStemSet("xxlimited", "_ctypes_test", "_test*")
+TEST_DIRS_ONLY = FileNameSet("test", "tests")
+
+IDLE_DIRS_ONLY = FileNameSet("idlelib")
+
+TCLTK_PYDS_ONLY = FileStemSet("tcl*", "tk*", "_tkinter")
+TCLTK_DIRS_ONLY = FileNameSet("tkinter", "turtledemo")
+TCLTK_FILES_ONLY = FileNameSet("turtle.py")
+
+VENV_DIRS_ONLY = FileNameSet("venv", "ensurepip")
+
+EXCLUDE_FROM_PYDS = FileStemSet("python*", "pyshellext")
+EXCLUDE_FROM_LIB = FileNameSet("*.pyc", "__pycache__", "*.pickle")
+EXCLUDE_FROM_PACKAGED_LIB = FileNameSet("readme.txt")
+EXCLUDE_FROM_COMPILE = FileNameSet("badsyntax_*", "bad_*")
+EXCLUDE_FROM_CATALOG = FileSuffixSet(".exe", ".pyd", ".dll")
+
+REQUIRED_DLLS = FileStemSet("libcrypto*", "libssl*")
+
+LIB2TO3_GRAMMAR_FILES = FileNameSet("Grammar.txt", "PatternGrammar.txt")
+
+PY_FILES = FileSuffixSet(".py")
+PYC_FILES = FileSuffixSet(".pyc")
+CAT_FILES = FileSuffixSet(".cat")
+CDF_FILES = FileSuffixSet(".cdf")
+
+DATA_DIRS = FileNameSet("data")
+
+TOOLS_DIRS = FileNameSet("scripts", "i18n", "pynche", "demo", "parser")
+TOOLS_FILES = FileSuffixSet(".py", ".pyw", ".txt")
+
+
+def get_lib_layout(ns):
+    def _c(f):
+        if f in EXCLUDE_FROM_LIB:
+            return False
+        if f.is_dir():
+            if f in TEST_DIRS_ONLY:
+                return ns.include_tests
+            if f in TCLTK_DIRS_ONLY:
+                return ns.include_tcltk
+            if f in IDLE_DIRS_ONLY:
+                return ns.include_idle
+            if f in VENV_DIRS_ONLY:
+                return ns.include_venv
+        else:
+            if f in TCLTK_FILES_ONLY:
+                return ns.include_tcltk
+            if f in BDIST_WININST_FILES_ONLY:
+                return ns.include_bdist_wininst
+        return True
+
+    for dest, src in rglob(ns.source / "Lib", "**/*", _c):
+        yield dest, src
+
+    if not ns.include_bdist_wininst:
+        src = ns.source / BDIST_WININST_STUB
+        yield Path("distutils/command/bdist_wininst.py"), src
+
+
+def get_tcltk_lib(ns):
+    if not ns.include_tcltk:
+        return
+
+    tcl_lib = os.getenv("TCL_LIBRARY")
+    if not tcl_lib or not os.path.isdir(tcl_lib):
+        try:
+            with open(ns.build / "TCL_LIBRARY.env", "r", encoding="utf-8-sig") as f:
+                tcl_lib = f.read().strip()
+        except FileNotFoundError:
+            pass
+        if not tcl_lib or not os.path.isdir(tcl_lib):
+            warn("Failed to find TCL_LIBRARY")
+            return
+
+    for dest, src in rglob(Path(tcl_lib).parent, "**/*"):
+        yield "tcl/{}".format(dest), src
+
+
+def get_layout(ns):
+    def in_build(f, dest="", new_name=None):
+        n, _, x = f.rpartition(".")
+        n = new_name or n
+        src = ns.build / f
+        if ns.debug and src not in REQUIRED_DLLS:
+            if not src.stem.endswith("_d"):
+                src = src.parent / (src.stem + "_d" + src.suffix)
+            if not n.endswith("_d"):
+                n += "_d"
+                f = n + "." + x
+        yield dest + n + "." + x, src
+        if ns.include_symbols:
+            pdb = src.with_suffix(".pdb")
+            if pdb.is_file():
+                yield dest + n + ".pdb", pdb
+        if ns.include_dev:
+            lib = src.with_suffix(".lib")
+            if lib.is_file():
+                yield "libs/" + n + ".lib", lib
+
+    if ns.include_appxmanifest:
+        yield from in_build("python_uwp.exe", new_name="python")
+        yield from in_build("pythonw_uwp.exe", new_name="pythonw")
+    else:
+        yield from in_build("python.exe", new_name="python")
+        yield from in_build("pythonw.exe", new_name="pythonw")
+
+    yield from in_build(PYTHON_DLL_NAME)
+
+    if ns.include_launchers and ns.include_appxmanifest:
+        if ns.include_pip:
+            yield from in_build("python_uwp.exe", new_name="pip")
+        if ns.include_idle:
+            yield from in_build("pythonw_uwp.exe", new_name="idle")
+
+    if ns.include_stable:
+        yield from in_build(PYTHON_STABLE_DLL_NAME)
+
+    for dest, src in rglob(ns.build, "vcruntime*.dll"):
+        yield dest, src
+
+    for dest, src in rglob(ns.build, ("*.pyd", "*.dll")):
+        if src.stem.endswith("_d") != bool(ns.debug) and src not in REQUIRED_DLLS:
+            continue
+        if src in EXCLUDE_FROM_PYDS:
+            continue
+        if src in TEST_PYDS_ONLY and not ns.include_tests:
+            continue
+        if src in TCLTK_PYDS_ONLY and not ns.include_tcltk:
+            continue
+
+        yield from in_build(src.name, dest="" if ns.flat_dlls else "DLLs/")
+
+    if ns.zip_lib:
+        zip_name = PYTHON_ZIP_NAME
+        yield zip_name, ns.temp / zip_name
+    else:
+        for dest, src in get_lib_layout(ns):
+            yield "Lib/{}".format(dest), src
+
+        if ns.include_venv:
+            yield from in_build("venvlauncher.exe", "Lib/venv/scripts/nt/", "python")
+            yield from in_build("venvwlauncher.exe", "Lib/venv/scripts/nt/", "pythonw")
+
+    if ns.include_tools:
+
+        def _c(d):
+            if d.is_dir():
+                return d in TOOLS_DIRS
+            return d in TOOLS_FILES
+
+        for dest, src in rglob(ns.source / "Tools", "**/*", _c):
+            yield "Tools/{}".format(dest), src
+
+    if ns.include_underpth:
+        yield PYTHON_PTH_NAME, ns.temp / PYTHON_PTH_NAME
+
+    if ns.include_dev:
+
+        def _c(d):
+            if d.is_dir():
+                return d.name != "internal"
+            return True
+
+        for dest, src in rglob(ns.source / "Include", "**/*.h", _c):
+            yield "include/{}".format(dest), src
+        src = ns.source / "PC" / "pyconfig.h"
+        yield "include/pyconfig.h", src
+
+    for dest, src in get_tcltk_lib(ns):
+        yield dest, src
+
+    if ns.include_pip:
+        pip_dir = get_pip_dir(ns)
+        if not pip_dir.is_dir():
+            log_warning("Failed to find {} - pip will not be included", pip_dir)
+        else:
+            pkg_root = "packages/{}" if ns.zip_lib else "Lib/site-packages/{}"
+            for dest, src in rglob(pip_dir, "**/*"):
+                if src in EXCLUDE_FROM_LIB or src in EXCLUDE_FROM_PACKAGED_LIB:
+                    continue
+                yield pkg_root.format(dest), src
+
+    if ns.include_chm:
+        for dest, src in rglob(ns.doc_build / "htmlhelp", PYTHON_CHM_NAME):
+            yield "Doc/{}".format(dest), src
+
+    if ns.include_html_doc:
+        for dest, src in rglob(ns.doc_build / "html", "**/*"):
+            yield "Doc/html/{}".format(dest), src
+
+    if ns.include_props:
+        for dest, src in get_props_layout(ns):
+            yield dest, src
+
+    for dest, src in get_appx_layout(ns):
+        yield dest, src
+
+    if ns.include_cat:
+        if ns.flat_dlls:
+            yield ns.include_cat.name, ns.include_cat
+        else:
+            yield "DLLs/{}".format(ns.include_cat.name), ns.include_cat
+
+
+def _compile_one_py(src, dest, name, optimize):
+    import py_compile
+
+    if dest is not None:
+        dest = str(dest)
+
+    try:
+        return Path(
+            py_compile.compile(
+                str(src),
+                dest,
+                str(name),
+                doraise=True,
+                optimize=optimize,
+                invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH,
+            )
+        )
+    except py_compile.PyCompileError:
+        log_warning("Failed to compile {}", src)
+        return None
+
+
+def _py_temp_compile(src, ns, dest_dir=None):
+    if not ns.precompile or src not in PY_FILES or src.parent in DATA_DIRS:
+        return None
+
+    dest = (dest_dir or ns.temp) / (src.stem + ".py")
+    return _compile_one_py(src, dest.with_suffix(".pyc"), dest, optimize=2)
+
+
+def _write_to_zip(zf, dest, src, ns):
+    pyc = _py_temp_compile(src, ns)
+    if pyc:
+        try:
+            zf.write(str(pyc), dest.with_suffix(".pyc"))
+        finally:
+            try:
+                pyc.unlink()
+            except:
+                log_exception("Failed to delete {}", pyc)
+        return
+
+    if src in LIB2TO3_GRAMMAR_FILES:
+        from lib2to3.pgen2.driver import load_grammar
+
+        tmp = ns.temp / src.name
+        try:
+            shutil.copy(src, tmp)
+            load_grammar(str(tmp))
+            for f in ns.temp.glob(src.stem + "*.pickle"):
+                zf.write(str(f), str(dest.parent / f.name))
+                try:
+                    f.unlink()
+                except:
+                    log_exception("Failed to delete {}", f)
+        except:
+            log_exception("Failed to compile {}", src)
+        finally:
+            try:
+                tmp.unlink()
+            except:
+                log_exception("Failed to delete {}", tmp)
+
+    zf.write(str(src), str(dest))
+
+
+def generate_source_files(ns):
+    if ns.zip_lib:
+        zip_name = PYTHON_ZIP_NAME
+        zip_path = ns.temp / zip_name
+        if zip_path.is_file():
+            zip_path.unlink()
+        elif zip_path.is_dir():
+            log_error(
+                "Cannot create zip file because a directory exists by the same name"
+            )
+            return
+        log_info("Generating {} in {}", zip_name, ns.temp)
+        ns.temp.mkdir(parents=True, exist_ok=True)
+        with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
+            for dest, src in get_lib_layout(ns):
+                _write_to_zip(zf, dest, src, ns)
+
+    if ns.include_underpth:
+        log_info("Generating {} in {}", PYTHON_PTH_NAME, ns.temp)
+        ns.temp.mkdir(parents=True, exist_ok=True)
+        with open(ns.temp / PYTHON_PTH_NAME, "w", encoding="utf-8") as f:
+            if ns.zip_lib:
+                print(PYTHON_ZIP_NAME, file=f)
+                if ns.include_pip:
+                    print("packages", file=f)
+            else:
+                print("Lib", file=f)
+                print("Lib/site-packages", file=f)
+            if not ns.flat_dlls:
+                print("DLLs", file=f)
+            print(".", file=f)
+            print(file=f)
+            print("# Uncomment to run site.main() automatically", file=f)
+            print("#import site", file=f)
+
+    if ns.include_appxmanifest:
+        log_info("Generating AppxManifest.xml in {}", ns.temp)
+        ns.temp.mkdir(parents=True, exist_ok=True)
+
+        with open(ns.temp / "AppxManifest.xml", "wb") as f:
+            f.write(get_appxmanifest(ns))
+
+        with open(ns.temp / "_resources.xml", "wb") as f:
+            f.write(get_resources_xml(ns))
+
+    if ns.include_pip:
+        pip_dir = get_pip_dir(ns)
+        if not (pip_dir / "pip").is_dir():
+            log_info("Extracting pip to {}", pip_dir)
+            pip_dir.mkdir(parents=True, exist_ok=True)
+            extract_pip_files(ns)
+
+    if ns.include_props:
+        log_info("Generating {} in {}", PYTHON_PROPS_NAME, ns.temp)
+        ns.temp.mkdir(parents=True, exist_ok=True)
+        with open(ns.temp / PYTHON_PROPS_NAME, "wb") as f:
+            f.write(get_props(ns))
+
+
+def _create_zip_file(ns):
+    if not ns.zip:
+        return None
+
+    if ns.zip.is_file():
+        try:
+            ns.zip.unlink()
+        except OSError:
+            log_exception("Unable to remove {}", ns.zip)
+            sys.exit(8)
+    elif ns.zip.is_dir():
+        log_error("Cannot create ZIP file because {} is a directory", ns.zip)
+        sys.exit(8)
+
+    ns.zip.parent.mkdir(parents=True, exist_ok=True)
+    return zipfile.ZipFile(ns.zip, "w", zipfile.ZIP_DEFLATED)
+
+
+def copy_files(files, ns):
+    if ns.copy:
+        ns.copy.mkdir(parents=True, exist_ok=True)
+
+    try:
+        total = len(files)
+    except TypeError:
+        total = None
+    count = 0
+
+    zip_file = _create_zip_file(ns)
+    try:
+        need_compile = []
+        in_catalog = []
+
+        for dest, src in files:
+            count += 1
+            if count % 10 == 0:
+                if total:
+                    log_info("Processed {:>4} of {} files", count, total)
+                else:
+                    log_info("Processed {} files", count)
+            log_debug("Processing {!s}", src)
+
+            if (
+                ns.precompile
+                and src in PY_FILES
+                and src not in EXCLUDE_FROM_COMPILE
+                and src.parent not in DATA_DIRS
+                and os.path.normcase(str(dest)).startswith(os.path.normcase("Lib"))
+            ):
+                if ns.copy:
+                    need_compile.append((dest, ns.copy / dest))
+                else:
+                    (ns.temp / "Lib" / dest).parent.mkdir(parents=True, exist_ok=True)
+                    shutil.copy2(src, ns.temp / "Lib" / dest)
+                    need_compile.append((dest, ns.temp / "Lib" / dest))
+
+            if src not in EXCLUDE_FROM_CATALOG:
+                in_catalog.append((src.name, src))
+
+            if ns.copy:
+                log_debug("Copy {} -> {}", src, ns.copy / dest)
+                (ns.copy / dest).parent.mkdir(parents=True, exist_ok=True)
+                try:
+                    shutil.copy2(src, ns.copy / dest)
+                except shutil.SameFileError:
+                    pass
+
+            if ns.zip:
+                log_debug("Zip {} into {}", src, ns.zip)
+                zip_file.write(src, str(dest))
+
+        if need_compile:
+            for dest, src in need_compile:
+                compiled = [
+                    _compile_one_py(src, None, dest, optimize=0),
+                    _compile_one_py(src, None, dest, optimize=1),
+                    _compile_one_py(src, None, dest, optimize=2),
+                ]
+                for c in compiled:
+                    if not c:
+                        continue
+                    cdest = Path(dest).parent / Path(c).relative_to(src.parent)
+                    if ns.zip:
+                        log_debug("Zip {} into {}", c, ns.zip)
+                        zip_file.write(c, str(cdest))
+                    in_catalog.append((cdest.name, cdest))
+
+        if ns.catalog:
+            # Just write out the CDF now. Compilation and signing is
+            # an extra step
+            log_info("Generating {}", ns.catalog)
+            ns.catalog.parent.mkdir(parents=True, exist_ok=True)
+            write_catalog(ns.catalog, in_catalog)
+
+    finally:
+        if zip_file:
+            zip_file.close()
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("-v", help="Increase verbosity", action="count")
+    parser.add_argument(
+        "-s",
+        "--source",
+        metavar="dir",
+        help="The directory containing the repository root",
+        type=Path,
+        default=None,
+    )
+    parser.add_argument(
+        "-b", "--build", metavar="dir", help="Specify the build directory", type=Path
+    )
+    parser.add_argument(
+        "--doc-build",
+        metavar="dir",
+        help="Specify the docs build directory",
+        type=Path,
+        default=None,
+    )
+    parser.add_argument(
+        "--copy",
+        metavar="directory",
+        help="The name of the directory to copy an extracted layout to",
+        type=Path,
+        default=None,
+    )
+    parser.add_argument(
+        "--zip",
+        metavar="file",
+        help="The ZIP file to write all files to",
+        type=Path,
+        default=None,
+    )
+    parser.add_argument(
+        "--catalog",
+        metavar="file",
+        help="The CDF file to write catalog entries to",
+        type=Path,
+        default=None,
+    )
+    parser.add_argument(
+        "--log",
+        metavar="file",
+        help="Write all operations to the specified file",
+        type=Path,
+        default=None,
+    )
+    parser.add_argument(
+        "-t",
+        "--temp",
+        metavar="file",
+        help="A temporary working directory",
+        type=Path,
+        default=None,
+    )
+    parser.add_argument(
+        "-d", "--debug", help="Include debug build", action="store_true"
+    )
+    parser.add_argument(
+        "-p",
+        "--precompile",
+        help="Include .pyc files instead of .py",
+        action="store_true",
+    )
+    parser.add_argument(
+        "-z", "--zip-lib", help="Include library in a ZIP file", action="store_true"
+    )
+    parser.add_argument(
+        "--flat-dlls", help="Does not create a DLLs directory", action="store_true"
+    )
+    parser.add_argument(
+        "-a",
+        "--include-all",
+        help="Include all optional components",
+        action="store_true",
+    )
+    parser.add_argument(
+        "--include-cat",
+        metavar="file",
+        help="Specify the catalog file to include",
+        type=Path,
+        default=None,
+    )
+    for opt, help in get_argparse_options():
+        parser.add_argument(opt, help=help, action="store_true")
+
+    ns = parser.parse_args()
+    update_presets(ns)
+
+    ns.source = ns.source or (Path(__file__).resolve().parent.parent.parent)
+    ns.build = ns.build or Path(sys.executable).parent
+    ns.temp = ns.temp or Path(tempfile.mkdtemp())
+    ns.doc_build = ns.doc_build or (ns.source / "Doc" / "build")
+    if not ns.source.is_absolute():
+        ns.source = (Path.cwd() / ns.source).resolve()
+    if not ns.build.is_absolute():
+        ns.build = (Path.cwd() / ns.build).resolve()
+    if not ns.temp.is_absolute():
+        ns.temp = (Path.cwd() / ns.temp).resolve()
+    if not ns.doc_build.is_absolute():
+        ns.doc_build = (Path.cwd() / ns.doc_build).resolve()
+    if ns.include_cat and not ns.include_cat.is_absolute():
+        ns.include_cat = (Path.cwd() / ns.include_cat).resolve()
+
+    if ns.copy and not ns.copy.is_absolute():
+        ns.copy = (Path.cwd() / ns.copy).resolve()
+    if ns.zip and not ns.zip.is_absolute():
+        ns.zip = (Path.cwd() / ns.zip).resolve()
+    if ns.catalog and not ns.catalog.is_absolute():
+        ns.catalog = (Path.cwd() / ns.catalog).resolve()
+
+    configure_logger(ns)
+
+    log_info(
+        """OPTIONS
+Source: {ns.source}
+Build:  {ns.build}
+Temp:   {ns.temp}
+
+Copy to: {ns.copy}
+Zip to:  {ns.zip}
+Catalog: {ns.catalog}""",
+        ns=ns,
+    )
+
+    if ns.include_idle and not ns.include_tcltk:
+        log_warning("Assuming --include-tcltk to support --include-idle")
+        ns.include_tcltk = True
+
+    try:
+        generate_source_files(ns)
+        files = list(get_layout(ns))
+        copy_files(files, ns)
+    except KeyboardInterrupt:
+        log_info("Interrupted by Ctrl+C")
+        return 3
+    except SystemExit:
+        raise
+    except:
+        log_exception("Unhandled error")
+
+    if error_was_logged():
+        log_error("Errors occurred.")
+        return 1
+
+
+if __name__ == "__main__":
+    sys.exit(int(main() or 0))
diff --git a/PC/layout/support/__init__.py b/PC/layout/support/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/PC/layout/support/appxmanifest.py b/PC/layout/support/appxmanifest.py
new file mode 100644 (file)
index 0000000..c5dda70
--- /dev/null
@@ -0,0 +1,487 @@
+"""
+File generation for APPX/MSIX manifests.
+"""
+
+__author__ = "Steve Dower <steve.dower@python.org>"
+__version__ = "3.8"
+
+
+import collections
+import ctypes
+import io
+import os
+import sys
+
+from pathlib import Path, PureWindowsPath
+from xml.etree import ElementTree as ET
+
+from .constants import *
+
+__all__ = []
+
+
+def public(f):
+    __all__.append(f.__name__)
+    return f
+
+
+APPX_DATA = dict(
+    Name="PythonSoftwareFoundation.Python.{}".format(VER_DOT),
+    Version="{}.{}.{}.0".format(VER_MAJOR, VER_MINOR, VER_FIELD3),
+    Publisher=os.getenv(
+        "APPX_DATA_PUBLISHER", "CN=4975D53F-AA7E-49A5-8B49-EA4FDC1BB66B"
+    ),
+    DisplayName="Python {}".format(VER_DOT),
+    Description="The Python {} runtime and console.".format(VER_DOT),
+    ProcessorArchitecture="x64" if IS_X64 else "x86",
+)
+
+PYTHON_VE_DATA = dict(
+    DisplayName="Python {}".format(VER_DOT),
+    Description="Python interactive console",
+    Square150x150Logo="_resources/pythonx150.png",
+    Square44x44Logo="_resources/pythonx44.png",
+    BackgroundColor="transparent",
+)
+
+PYTHONW_VE_DATA = dict(
+    DisplayName="Python {} (Windowed)".format(VER_DOT),
+    Description="Python windowed app launcher",
+    Square150x150Logo="_resources/pythonwx150.png",
+    Square44x44Logo="_resources/pythonwx44.png",
+    BackgroundColor="transparent",
+    AppListEntry="none",
+)
+
+PIP_VE_DATA = dict(
+    DisplayName="pip (Python {})".format(VER_DOT),
+    Description="pip package manager for Python {}".format(VER_DOT),
+    Square150x150Logo="_resources/pythonx150.png",
+    Square44x44Logo="_resources/pythonx44.png",
+    BackgroundColor="transparent",
+    AppListEntry="none",
+)
+
+IDLE_VE_DATA = dict(
+    DisplayName="IDLE (Python {})".format(VER_DOT),
+    Description="IDLE editor for Python {}".format(VER_DOT),
+    Square150x150Logo="_resources/pythonwx150.png",
+    Square44x44Logo="_resources/pythonwx44.png",
+    BackgroundColor="transparent",
+)
+
+APPXMANIFEST_NS = {
+    "": "http://schemas.microsoft.com/appx/manifest/foundation/windows10",
+    "m": "http://schemas.microsoft.com/appx/manifest/foundation/windows10",
+    "uap": "http://schemas.microsoft.com/appx/manifest/uap/windows10",
+    "rescap": "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities",
+    "rescap4": "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities/4",
+    "desktop4": "http://schemas.microsoft.com/appx/manifest/desktop/windows10/4",
+    "desktop6": "http://schemas.microsoft.com/appx/manifest/desktop/windows10/6",
+    "uap3": "http://schemas.microsoft.com/appx/manifest/uap/windows10/3",
+    "uap4": "http://schemas.microsoft.com/appx/manifest/uap/windows10/4",
+    "uap5": "http://schemas.microsoft.com/appx/manifest/uap/windows10/5",
+}
+
+APPXMANIFEST_TEMPLATE = """<?xml version="1.0" encoding="utf-8"?>
+<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
+    xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
+    xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
+    xmlns:rescap4="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities/4"
+    xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
+    xmlns:uap4="http://schemas.microsoft.com/appx/manifest/uap/windows10/4"
+    xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5">
+    <Identity Name=""
+              Version=""
+              Publisher=""
+              ProcessorArchitecture="" />
+    <Properties>
+        <DisplayName></DisplayName>
+        <PublisherDisplayName>Python Software Foundation</PublisherDisplayName>
+        <Description></Description>
+        <Logo>_resources/pythonx50.png</Logo>
+    </Properties>
+    <Resources>
+        <Resource Language="en-US" />
+    </Resources>
+    <Dependencies>
+        <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="" />
+    </Dependencies>
+    <Capabilities>
+        <rescap:Capability Name="runFullTrust"/>
+    </Capabilities>
+    <Applications>
+    </Applications>
+    <Extensions>
+    </Extensions>
+</Package>"""
+
+
+RESOURCES_XML_TEMPLATE = r"""<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--This file is input for makepri.exe. It should be excluded from the final package.-->
+<resources targetOsVersion="10.0.0" majorVersion="1">
+    <packaging>
+        <autoResourcePackage qualifier="Language"/>
+        <autoResourcePackage qualifier="Scale"/>
+        <autoResourcePackage qualifier="DXFeatureLevel"/>
+    </packaging>
+    <index root="\" startIndexAt="\">
+        <default>
+            <qualifier name="Language" value="en-US"/>
+            <qualifier name="Contrast" value="standard"/>
+            <qualifier name="Scale" value="100"/>
+            <qualifier name="HomeRegion" value="001"/>
+            <qualifier name="TargetSize" value="256"/>
+            <qualifier name="LayoutDirection" value="LTR"/>
+            <qualifier name="Theme" value="dark"/>
+            <qualifier name="AlternateForm" value=""/>
+            <qualifier name="DXFeatureLevel" value="DX9"/>
+            <qualifier name="Configuration" value=""/>
+            <qualifier name="DeviceFamily" value="Universal"/>
+            <qualifier name="Custom" value=""/>
+        </default>
+        <indexer-config type="folder" foldernameAsQualifier="true" filenameAsQualifier="true" qualifierDelimiter="$"/>
+        <indexer-config type="resw" convertDotsToSlashes="true" initialPath=""/>
+        <indexer-config type="resjson" initialPath=""/>
+        <indexer-config type="PRI"/>
+    </index>
+</resources>"""
+
+
+SCCD_FILENAME = "PC/classicAppCompat.sccd"
+
+REGISTRY = {
+    "HKCU\\Software\\Python\\PythonCore": {
+        VER_DOT: {
+            "DisplayName": APPX_DATA["DisplayName"],
+            "SupportUrl": "https://www.python.org/",
+            "SysArchitecture": "64bit" if IS_X64 else "32bit",
+            "SysVersion": VER_DOT,
+            "Version": "{}.{}.{}".format(VER_MAJOR, VER_MINOR, VER_MICRO),
+            "InstallPath": {
+                # I have no idea why the trailing spaces are needed, but they seem to be needed.
+                "": "[{AppVPackageRoot}][                    ]",
+                "ExecutablePath": "[{AppVPackageRoot}]python.exe[                    ]",
+                "WindowedExecutablePath": "[{AppVPackageRoot}]pythonw.exe[                    ]",
+            },
+            "Help": {
+                "Main Python Documentation": {
+                    "_condition": lambda ns: ns.include_chm,
+                    "": "[{{AppVPackageRoot}}]Doc\\{}[                    ]".format(
+                        PYTHON_CHM_NAME
+                    ),
+                },
+                "Local Python Documentation": {
+                    "_condition": lambda ns: ns.include_html_doc,
+                    "": "[{AppVPackageRoot}]Doc\\html\\index.html[                    ]",
+                },
+                "Online Python Documentation": {
+                    "": "https://docs.python.org/{}".format(VER_DOT)
+                },
+            },
+            "Idle": {
+                "_condition": lambda ns: ns.include_idle,
+                "": "[{AppVPackageRoot}]Lib\\idlelib\\idle.pyw[                    ]",
+            },
+        }
+    }
+}
+
+
+def get_packagefamilyname(name, publisher_id):
+    class PACKAGE_ID(ctypes.Structure):
+        _fields_ = [
+            ("reserved", ctypes.c_uint32),
+            ("processorArchitecture", ctypes.c_uint32),
+            ("version", ctypes.c_uint64),
+            ("name", ctypes.c_wchar_p),
+            ("publisher", ctypes.c_wchar_p),
+            ("resourceId", ctypes.c_wchar_p),
+            ("publisherId", ctypes.c_wchar_p),
+        ]
+        _pack_ = 4
+
+    pid = PACKAGE_ID(0, 0, 0, name, publisher_id, None, None)
+    result = ctypes.create_unicode_buffer(256)
+    result_len = ctypes.c_uint32(256)
+    r = ctypes.windll.kernel32.PackageFamilyNameFromId(
+        pid, ctypes.byref(result_len), result
+    )
+    if r:
+        raise OSError(r, "failed to get package family name")
+    return result.value[: result_len.value]
+
+
+def _fixup_sccd(ns, sccd, new_hash=None):
+    if not new_hash:
+        return sccd
+
+    NS = dict(s="http://schemas.microsoft.com/appx/2016/sccd")
+    with open(sccd, "rb") as f:
+        xml = ET.parse(f)
+
+    pfn = get_packagefamilyname(APPX_DATA["Name"], APPX_DATA["Publisher"])
+
+    ae = xml.find("s:AuthorizedEntities", NS)
+    ae.clear()
+
+    e = ET.SubElement(ae, ET.QName(NS["s"], "AuthorizedEntity"))
+    e.set("AppPackageFamilyName", pfn)
+    e.set("CertificateSignatureHash", new_hash)
+
+    for e in xml.findall("s:Catalog", NS):
+        e.text = "FFFF"
+
+    sccd = ns.temp / sccd.name
+    sccd.parent.mkdir(parents=True, exist_ok=True)
+    with open(sccd, "wb") as f:
+        xml.write(f, encoding="utf-8")
+
+    return sccd
+
+
+@public
+def get_appx_layout(ns):
+    if not ns.include_appxmanifest:
+        return
+
+    yield "AppxManifest.xml", ns.temp / "AppxManifest.xml"
+    yield "_resources.xml", ns.temp / "_resources.xml"
+    icons = ns.source / "PC" / "icons"
+    yield "_resources/pythonx44.png", icons / "pythonx44.png"
+    yield "_resources/pythonx44$targetsize-44_altform-unplated.png", icons / "pythonx44.png"
+    yield "_resources/pythonx50.png", icons / "pythonx50.png"
+    yield "_resources/pythonx50$targetsize-50_altform-unplated.png", icons / "pythonx50.png"
+    yield "_resources/pythonx150.png", icons / "pythonx150.png"
+    yield "_resources/pythonx150$targetsize-150_altform-unplated.png", icons / "pythonx150.png"
+    yield "_resources/pythonwx44.png", icons / "pythonwx44.png"
+    yield "_resources/pythonwx44$targetsize-44_altform-unplated.png", icons / "pythonwx44.png"
+    yield "_resources/pythonwx150.png", icons / "pythonwx150.png"
+    yield "_resources/pythonwx150$targetsize-150_altform-unplated.png", icons / "pythonwx150.png"
+    sccd = ns.source / SCCD_FILENAME
+    if sccd.is_file():
+        # This should only be set for side-loading purposes.
+        sccd = _fixup_sccd(ns, sccd, os.getenv("APPX_DATA_SHA256"))
+        yield sccd.name, sccd
+
+
+def find_or_add(xml, element, attr=None, always_add=False):
+    if always_add:
+        e = None
+    else:
+        q = element
+        if attr:
+            q += "[@{}='{}']".format(*attr)
+        e = xml.find(q, APPXMANIFEST_NS)
+    if e is None:
+        prefix, _, name = element.partition(":")
+        name = ET.QName(APPXMANIFEST_NS[prefix or ""], name)
+        e = ET.SubElement(xml, name)
+        if attr:
+            e.set(*attr)
+    return e
+
+
+def _get_app(xml, appid):
+    if appid:
+        app = xml.find(
+            "m:Applications/m:Application[@Id='{}']".format(appid), APPXMANIFEST_NS
+        )
+        if app is None:
+            raise LookupError(appid)
+    else:
+        app = xml
+    return app
+
+
+def add_visual(xml, appid, data):
+    app = _get_app(xml, appid)
+    e = find_or_add(app, "uap:VisualElements")
+    for i in data.items():
+        e.set(*i)
+    return e
+
+
+def add_alias(xml, appid, alias, subsystem="windows"):
+    app = _get_app(xml, appid)
+    e = find_or_add(app, "m:Extensions")
+    e = find_or_add(e, "uap5:Extension", ("Category", "windows.appExecutionAlias"))
+    e = find_or_add(e, "uap5:AppExecutionAlias")
+    e.set(ET.QName(APPXMANIFEST_NS["desktop4"], "Subsystem"), subsystem)
+    e = find_or_add(e, "uap5:ExecutionAlias", ("Alias", alias))
+
+
+def add_file_type(xml, appid, name, suffix, parameters='"%1"'):
+    app = _get_app(xml, appid)
+    e = find_or_add(app, "m:Extensions")
+    e = find_or_add(e, "uap3:Extension", ("Category", "windows.fileTypeAssociation"))
+    e = find_or_add(e, "uap3:FileTypeAssociation", ("Name", name))
+    e.set("Parameters", parameters)
+    e = find_or_add(e, "uap:SupportedFileTypes")
+    if isinstance(suffix, str):
+        suffix = [suffix]
+    for s in suffix:
+        ET.SubElement(e, ET.QName(APPXMANIFEST_NS["uap"], "FileType")).text = s
+
+
+def add_application(
+    ns, xml, appid, executable, aliases, visual_element, subsystem, file_types
+):
+    node = xml.find("m:Applications", APPXMANIFEST_NS)
+    suffix = "_d.exe" if ns.debug else ".exe"
+    app = ET.SubElement(
+        node,
+        ET.QName(APPXMANIFEST_NS[""], "Application"),
+        {
+            "Id": appid,
+            "Executable": executable + suffix,
+            "EntryPoint": "Windows.FullTrustApplication",
+            ET.QName(APPXMANIFEST_NS["desktop4"], "SupportsMultipleInstances"): "true",
+        },
+    )
+    if visual_element:
+        add_visual(app, None, visual_element)
+    for alias in aliases:
+        add_alias(app, None, alias + suffix, subsystem)
+    if file_types:
+        add_file_type(app, None, *file_types)
+    return app
+
+
+def _get_registry_entries(ns, root="", d=None):
+    r = root if root else PureWindowsPath("")
+    if d is None:
+        d = REGISTRY
+    for key, value in d.items():
+        if key == "_condition":
+            continue
+        elif isinstance(value, dict):
+            cond = value.get("_condition")
+            if cond and not cond(ns):
+                continue
+            fullkey = r
+            for part in PureWindowsPath(key).parts:
+                fullkey /= part
+                if len(fullkey.parts) > 1:
+                    yield str(fullkey), None, None
+            yield from _get_registry_entries(ns, fullkey, value)
+        elif len(r.parts) > 1:
+            yield str(r), key, value
+
+
+def add_registry_entries(ns, xml):
+    e = find_or_add(xml, "m:Extensions")
+    e = find_or_add(e, "rescap4:Extension")
+    e.set("Category", "windows.classicAppCompatKeys")
+    e.set("EntryPoint", "Windows.FullTrustApplication")
+    e = ET.SubElement(e, ET.QName(APPXMANIFEST_NS["rescap4"], "ClassicAppCompatKeys"))
+    for name, valuename, value in _get_registry_entries(ns):
+        k = ET.SubElement(
+            e, ET.QName(APPXMANIFEST_NS["rescap4"], "ClassicAppCompatKey")
+        )
+        k.set("Name", name)
+        if value:
+            k.set("ValueName", valuename)
+            k.set("Value", value)
+            k.set("ValueType", "REG_SZ")
+
+
+def disable_registry_virtualization(xml):
+    e = find_or_add(xml, "m:Properties")
+    e = find_or_add(e, "desktop6:RegistryWriteVirtualization")
+    e.text = "disabled"
+    e = find_or_add(xml, "m:Capabilities")
+    e = find_or_add(e, "rescap:Capability", ("Name", "unvirtualizedResources"))
+
+
+@public
+def get_appxmanifest(ns):
+    for k, v in APPXMANIFEST_NS.items():
+        ET.register_namespace(k, v)
+    ET.register_namespace("", APPXMANIFEST_NS["m"])
+
+    xml = ET.parse(io.StringIO(APPXMANIFEST_TEMPLATE))
+    NS = APPXMANIFEST_NS
+    QN = ET.QName
+
+    node = xml.find("m:Identity", NS)
+    for k in node.keys():
+        value = APPX_DATA.get(k)
+        if value:
+            node.set(k, value)
+
+    for node in xml.find("m:Properties", NS):
+        value = APPX_DATA.get(node.tag.rpartition("}")[2])
+        if value:
+            node.text = value
+
+    winver = sys.getwindowsversion()[:3]
+    if winver < (10, 0, 17763):
+        winver = 10, 0, 17763
+    find_or_add(xml, "m:Dependencies/m:TargetDeviceFamily").set(
+        "MaxVersionTested", "{}.{}.{}.0".format(*winver)
+    )
+
+    if winver > (10, 0, 17763):
+        disable_registry_virtualization(xml)
+
+    app = add_application(
+        ns,
+        xml,
+        "Python",
+        "python",
+        ["python", "python{}".format(VER_MAJOR), "python{}".format(VER_DOT)],
+        PYTHON_VE_DATA,
+        "console",
+        ("python.file", [".py"]),
+    )
+
+    add_application(
+        ns,
+        xml,
+        "PythonW",
+        "pythonw",
+        ["pythonw", "pythonw{}".format(VER_MAJOR), "pythonw{}".format(VER_DOT)],
+        PYTHONW_VE_DATA,
+        "windows",
+        ("python.windowedfile", [".pyw"]),
+    )
+
+    if ns.include_pip and ns.include_launchers:
+        add_application(
+            ns,
+            xml,
+            "Pip",
+            "pip",
+            ["pip", "pip{}".format(VER_MAJOR), "pip{}".format(VER_DOT)],
+            PIP_VE_DATA,
+            "console",
+            ("python.wheel", [".whl"], 'install "%1"'),
+        )
+
+    if ns.include_idle and ns.include_launchers:
+        add_application(
+            ns,
+            xml,
+            "Idle",
+            "idle",
+            ["idle", "idle{}".format(VER_MAJOR), "idle{}".format(VER_DOT)],
+            IDLE_VE_DATA,
+            "windows",
+            None,
+        )
+
+    if (ns.source / SCCD_FILENAME).is_file():
+        add_registry_entries(ns, xml)
+        node = xml.find("m:Capabilities", NS)
+        node = ET.SubElement(node, QN(NS["uap4"], "CustomCapability"))
+        node.set("Name", "Microsoft.classicAppCompat_8wekyb3d8bbwe")
+
+    buffer = io.BytesIO()
+    xml.write(buffer, encoding="utf-8", xml_declaration=True)
+    return buffer.getbuffer()
+
+
+@public
+def get_resources_xml(ns):
+    return RESOURCES_XML_TEMPLATE.encode("utf-8")
diff --git a/PC/layout/support/catalog.py b/PC/layout/support/catalog.py
new file mode 100644 (file)
index 0000000..4312118
--- /dev/null
@@ -0,0 +1,44 @@
+"""
+File generation for catalog signing non-binary contents.
+"""
+
+__author__ = "Steve Dower <steve.dower@python.org>"
+__version__ = "3.8"
+
+
+import sys
+
+__all__ = ["PYTHON_CAT_NAME", "PYTHON_CDF_NAME"]
+
+
+def public(f):
+    __all__.append(f.__name__)
+    return f
+
+
+PYTHON_CAT_NAME = "python.cat"
+PYTHON_CDF_NAME = "python.cdf"
+
+
+CATALOG_TEMPLATE = r"""[CatalogHeader]
+Name={target.stem}.cat
+ResultDir={target.parent}
+PublicVersion=1
+CatalogVersion=2
+HashAlgorithms=SHA256
+PageHashes=false
+EncodingType=
+
+[CatalogFiles]
+"""
+
+
+def can_sign(file):
+    return file.is_file() and file.stat().st_size
+
+
+@public
+def write_catalog(target, files):
+    with target.open("w", encoding="utf-8") as cat:
+        cat.write(CATALOG_TEMPLATE.format(target=target))
+        cat.writelines("<HASH>{}={}\n".format(n, f) for n, f in files if can_sign(f))
diff --git a/PC/layout/support/constants.py b/PC/layout/support/constants.py
new file mode 100644 (file)
index 0000000..88ea410
--- /dev/null
@@ -0,0 +1,28 @@
+"""
+Constants for generating the layout.
+"""
+
+__author__ = "Steve Dower <steve.dower@python.org>"
+__version__ = "3.8"
+
+import struct
+import sys
+
+VER_MAJOR, VER_MINOR, VER_MICRO, VER_FIELD4 = struct.pack(">i", sys.hexversion)
+VER_FIELD3 = VER_MICRO << 8 | VER_FIELD4
+VER_NAME = {"alpha": "a", "beta": "b", "rc": "rc"}.get(
+    sys.version_info.releaselevel, ""
+)
+VER_SERIAL = sys.version_info.serial if VER_NAME else ""
+VER_DOT = "{}.{}".format(VER_MAJOR, VER_MINOR)
+
+PYTHON_DLL_NAME = "python{}{}.dll".format(VER_MAJOR, VER_MINOR)
+PYTHON_STABLE_DLL_NAME = "python{}.dll".format(VER_MAJOR)
+PYTHON_ZIP_NAME = "python{}{}.zip".format(VER_MAJOR, VER_MINOR)
+PYTHON_PTH_NAME = "python{}{}._pth".format(VER_MAJOR, VER_MINOR)
+
+PYTHON_CHM_NAME = "python{}{}{}{}{}.chm".format(
+    VER_MAJOR, VER_MINOR, VER_MICRO, VER_NAME, VER_SERIAL
+)
+
+IS_X64 = sys.maxsize > 2 ** 32
diff --git a/PC/layout/support/distutils.command.bdist_wininst.py b/PC/layout/support/distutils.command.bdist_wininst.py
new file mode 100644 (file)
index 0000000..6e9b49f
--- /dev/null
@@ -0,0 +1,25 @@
+"""distutils.command.bdist_wininst
+
+Suppress the 'bdist_wininst' command, while still allowing
+setuptools to import it without breaking."""
+
+from distutils.core import Command
+from distutils.errors import DistutilsPlatformError
+
+
+class bdist_wininst(Command):
+    description = "create an executable installer for MS Windows"
+
+    # Marker for tests that we have the unsupported bdist_wininst
+    _unsupported = True
+
+    def initialize_options(self):
+        pass
+
+    def finalize_options(self):
+        pass
+
+    def run(self):
+        raise DistutilsPlatformError(
+            "bdist_wininst is not supported in this Python distribution"
+        )
diff --git a/PC/layout/support/filesets.py b/PC/layout/support/filesets.py
new file mode 100644 (file)
index 0000000..47f727c
--- /dev/null
@@ -0,0 +1,100 @@
+"""
+File sets and globbing helper for make_layout.
+"""
+
+__author__ = "Steve Dower <steve.dower@python.org>"
+__version__ = "3.8"
+
+import os
+
+
+class FileStemSet:
+    def __init__(self, *patterns):
+        self._names = set()
+        self._prefixes = []
+        self._suffixes = []
+        for p in map(os.path.normcase, patterns):
+            if p.endswith("*"):
+                self._prefixes.append(p[:-1])
+            elif p.startswith("*"):
+                self._suffixes.append(p[1:])
+            else:
+                self._names.add(p)
+
+    def _make_name(self, f):
+        return os.path.normcase(f.stem)
+
+    def __contains__(self, f):
+        bn = self._make_name(f)
+        return (
+            bn in self._names
+            or any(map(bn.startswith, self._prefixes))
+            or any(map(bn.endswith, self._suffixes))
+        )
+
+
+class FileNameSet(FileStemSet):
+    def _make_name(self, f):
+        return os.path.normcase(f.name)
+
+
+class FileSuffixSet:
+    def __init__(self, *patterns):
+        self._names = set()
+        self._prefixes = []
+        self._suffixes = []
+        for p in map(os.path.normcase, patterns):
+            if p.startswith("*."):
+                self._names.add(p[1:])
+            elif p.startswith("*"):
+                self._suffixes.append(p[1:])
+            elif p.endswith("*"):
+                self._prefixes.append(p[:-1])
+            elif p.startswith("."):
+                self._names.add(p)
+            else:
+                self._names.add("." + p)
+
+    def _make_name(self, f):
+        return os.path.normcase(f.suffix)
+
+    def __contains__(self, f):
+        bn = self._make_name(f)
+        return (
+            bn in self._names
+            or any(map(bn.startswith, self._prefixes))
+            or any(map(bn.endswith, self._suffixes))
+        )
+
+
+def _rglob(root, pattern, condition):
+    dirs = [root]
+    recurse = pattern[:3] in {"**/", "**\\"}
+    if recurse:
+        pattern = pattern[3:]
+
+    while dirs:
+        d = dirs.pop(0)
+        if recurse:
+            dirs.extend(
+                filter(
+                    condition, (type(root)(f2) for f2 in os.scandir(d) if f2.is_dir())
+                )
+            )
+        yield from (
+            (f.relative_to(root), f)
+            for f in d.glob(pattern)
+            if f.is_file() and condition(f)
+        )
+
+
+def _return_true(f):
+    return True
+
+
+def rglob(root, patterns, condition=None):
+    if isinstance(patterns, tuple):
+        for p in patterns:
+            yield from _rglob(root, p, condition or _return_true)
+    else:
+        yield from _rglob(root, patterns, condition or _return_true)
diff --git a/PC/layout/support/logging.py b/PC/layout/support/logging.py
new file mode 100644 (file)
index 0000000..30869b9
--- /dev/null
@@ -0,0 +1,93 @@
+"""
+Logging support for make_layout.
+"""
+
+__author__ = "Steve Dower <steve.dower@python.org>"
+__version__ = "3.8"
+
+import logging
+import sys
+
+__all__ = []
+
+LOG = None
+HAS_ERROR = False
+
+
+def public(f):
+    __all__.append(f.__name__)
+    return f
+
+
+@public
+def configure_logger(ns):
+    global LOG
+    if LOG:
+        return
+
+    LOG = logging.getLogger("make_layout")
+    LOG.level = logging.DEBUG
+
+    if ns.v:
+        s_level = max(logging.ERROR - ns.v * 10, logging.DEBUG)
+        f_level = max(logging.WARNING - ns.v * 10, logging.DEBUG)
+    else:
+        s_level = logging.ERROR
+        f_level = logging.INFO
+
+    handler = logging.StreamHandler(sys.stdout)
+    handler.setFormatter(logging.Formatter("{levelname:8s} {message}", style="{"))
+    handler.setLevel(s_level)
+    LOG.addHandler(handler)
+
+    if ns.log:
+        handler = logging.FileHandler(ns.log, encoding="utf-8", delay=True)
+        handler.setFormatter(
+            logging.Formatter("[{asctime}]{levelname:8s}: {message}", style="{")
+        )
+        handler.setLevel(f_level)
+        LOG.addHandler(handler)
+
+
+class BraceMessage:
+    def __init__(self, fmt, *args, **kwargs):
+        self.fmt = fmt
+        self.args = args
+        self.kwargs = kwargs
+
+    def __str__(self):
+        return self.fmt.format(*self.args, **self.kwargs)
+
+
+@public
+def log_debug(msg, *args, **kwargs):
+    return LOG.debug(BraceMessage(msg, *args, **kwargs))
+
+
+@public
+def log_info(msg, *args, **kwargs):
+    return LOG.info(BraceMessage(msg, *args, **kwargs))
+
+
+@public
+def log_warning(msg, *args, **kwargs):
+    return LOG.warning(BraceMessage(msg, *args, **kwargs))
+
+
+@public
+def log_error(msg, *args, **kwargs):
+    global HAS_ERROR
+    HAS_ERROR = True
+    return LOG.error(BraceMessage(msg, *args, **kwargs))
+
+
+@public
+def log_exception(msg, *args, **kwargs):
+    global HAS_ERROR
+    HAS_ERROR = True
+    return LOG.exception(BraceMessage(msg, *args, **kwargs))
+
+
+@public
+def error_was_logged():
+    return HAS_ERROR
diff --git a/PC/layout/support/options.py b/PC/layout/support/options.py
new file mode 100644 (file)
index 0000000..76d9e34
--- /dev/null
@@ -0,0 +1,122 @@
+"""
+List of optional components.
+"""
+
+__author__ = "Steve Dower <steve.dower@python.org>"
+__version__ = "3.8"
+
+
+__all__ = []
+
+
+def public(f):
+    __all__.append(f.__name__)
+    return f
+
+
+OPTIONS = {
+    "stable": {"help": "stable ABI stub"},
+    "pip": {"help": "pip"},
+    "distutils": {"help": "distutils"},
+    "tcltk": {"help": "Tcl, Tk and tkinter"},
+    "idle": {"help": "Idle"},
+    "tests": {"help": "test suite"},
+    "tools": {"help": "tools"},
+    "venv": {"help": "venv"},
+    "dev": {"help": "headers and libs"},
+    "symbols": {"help": "symbols"},
+    "bdist-wininst": {"help": "bdist_wininst support"},
+    "underpth": {"help": "a python._pth file", "not-in-all": True},
+    "launchers": {"help": "specific launchers"},
+    "appxmanifest": {"help": "an appxmanifest"},
+    "props": {"help": "a python.props file"},
+    "chm": {"help": "the CHM documentation"},
+    "html-doc": {"help": "the HTML documentation"},
+}
+
+
+PRESETS = {
+    "appx": {
+        "help": "APPX package",
+        "options": [
+            "stable",
+            "pip",
+            "distutils",
+            "tcltk",
+            "idle",
+            "venv",
+            "dev",
+            "launchers",
+            "appxmanifest",
+            # XXX: Disabled for now "precompile",
+        ],
+    },
+    "nuget": {
+        "help": "nuget package",
+        "options": ["stable", "pip", "distutils", "dev", "props"],
+    },
+    "default": {
+        "help": "development kit package",
+        "options": [
+            "stable",
+            "pip",
+            "distutils",
+            "tcltk",
+            "idle",
+            "tests",
+            "tools",
+            "venv",
+            "dev",
+            "symbols",
+            "bdist-wininst",
+            "chm",
+        ],
+    },
+    "embed": {
+        "help": "embeddable package",
+        "options": ["stable", "zip-lib", "flat-dlls", "underpth", "precompile"],
+    },
+}
+
+
+@public
+def get_argparse_options():
+    for opt, info in OPTIONS.items():
+        help = "When specified, includes {}".format(info["help"])
+        if info.get("not-in-all"):
+            help = "{}. Not affected by --include-all".format(help)
+
+        yield "--include-{}".format(opt), help
+
+    for opt, info in PRESETS.items():
+        help = "When specified, includes default options for {}".format(info["help"])
+        yield "--preset-{}".format(opt), help
+
+
+def ns_get(ns, key, default=False):
+    return getattr(ns, key.replace("-", "_"), default)
+
+
+def ns_set(ns, key, value=True):
+    k1 = key.replace("-", "_")
+    k2 = "include_{}".format(k1)
+    if hasattr(ns, k2):
+        setattr(ns, k2, value)
+    elif hasattr(ns, k1):
+        setattr(ns, k1, value)
+    else:
+        raise AttributeError("no argument named '{}'".format(k1))
+
+
+@public
+def update_presets(ns):
+    for preset, info in PRESETS.items():
+        if ns_get(ns, "preset-{}".format(preset)):
+            for opt in info["options"]:
+                ns_set(ns, opt)
+
+    if ns.include_all:
+        for opt in OPTIONS:
+            if OPTIONS[opt].get("not-in-all"):
+                continue
+            ns_set(ns, opt)
diff --git a/PC/layout/support/pip.py b/PC/layout/support/pip.py
new file mode 100644 (file)
index 0000000..369a923
--- /dev/null
@@ -0,0 +1,79 @@
+"""
+Extraction and file list generation for pip.
+"""
+
+__author__ = "Steve Dower <steve.dower@python.org>"
+__version__ = "3.8"
+
+
+import os
+import shutil
+import subprocess
+import sys
+
+__all__ = []
+
+
+def public(f):
+    __all__.append(f.__name__)
+    return f
+
+
+@public
+def get_pip_dir(ns):
+    if ns.copy:
+        if ns.zip_lib:
+            return ns.copy / "packages"
+        return ns.copy / "Lib" / "site-packages"
+    else:
+        return ns.temp / "packages"
+
+
+@public
+def extract_pip_files(ns):
+    dest = get_pip_dir(ns)
+    dest.mkdir(parents=True, exist_ok=True)
+
+    src = ns.source / "Lib" / "ensurepip" / "_bundled"
+
+    ns.temp.mkdir(parents=True, exist_ok=True)
+    wheels = [shutil.copy(whl, ns.temp) for whl in src.glob("*.whl")]
+    search_path = os.pathsep.join(wheels)
+    if os.environ.get("PYTHONPATH"):
+        search_path += ";" + os.environ["PYTHONPATH"]
+
+    env = os.environ.copy()
+    env["PYTHONPATH"] = search_path
+
+    output = subprocess.check_output(
+        [
+            sys.executable,
+            "-m",
+            "pip",
+            "--no-color",
+            "install",
+            "pip",
+            "setuptools",
+            "--upgrade",
+            "--target",
+            str(dest),
+            "--no-index",
+            "--no-cache-dir",
+            "-f",
+            str(src),
+            "--only-binary",
+            ":all:",
+        ],
+        env=env,
+    )
+
+    try:
+        shutil.rmtree(dest / "bin")
+    except OSError:
+        pass
+
+    for file in wheels:
+        try:
+            os.remove(file)
+        except OSError:
+            pass
diff --git a/PC/layout/support/props.py b/PC/layout/support/props.py
new file mode 100644 (file)
index 0000000..3a047d2
--- /dev/null
@@ -0,0 +1,110 @@
+"""
+Provides .props file.
+"""
+
+import os
+
+from .constants import *
+
+__all__ = ["PYTHON_PROPS_NAME"]
+
+
+def public(f):
+    __all__.append(f.__name__)
+    return f
+
+
+PYTHON_PROPS_NAME = "python.props"
+
+PROPS_DATA = {
+    "PYTHON_TAG": VER_DOT,
+    "PYTHON_VERSION": os.getenv("PYTHON_NUSPEC_VERSION"),
+    "PYTHON_PLATFORM": os.getenv("PYTHON_PROPS_PLATFORM"),
+    "PYTHON_TARGET": "",
+}
+
+if not PROPS_DATA["PYTHON_VERSION"]:
+    if VER_NAME:
+        PROPS_DATA["PYTHON_VERSION"] = "{}.{}-{}{}".format(
+            VER_DOT, VER_MICRO, VER_NAME, VER_SERIAL
+        )
+    else:
+        PROPS_DATA["PYTHON_VERSION"] = "{}.{}".format(VER_DOT, VER_MICRO)
+
+if not PROPS_DATA["PYTHON_PLATFORM"]:
+    PROPS_DATA["PYTHON_PLATFORM"] = "x64" if IS_X64 else "Win32"
+
+PROPS_DATA["PYTHON_TARGET"] = "_GetPythonRuntimeFilesDependsOn{}{}_{}".format(
+    VER_MAJOR, VER_MINOR, PROPS_DATA["PYTHON_PLATFORM"]
+)
+
+PROPS_TEMPLATE = r"""<?xml version="1.0" encoding="utf-8"?>
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup Condition="$(Platform) == '{PYTHON_PLATFORM}'">
+    <PythonHome Condition="$(Configuration) == 'Debug'">$([msbuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), "python_d.exe")</PythonHome>
+    <PythonHome Condition="$(PythonHome) == ''">$([msbuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), "python.exe")</PythonHome>
+    <PythonInclude>$(PythonHome)\include</PythonInclude>
+    <PythonLibs>$(PythonHome)\libs</PythonLibs>
+    <PythonTag>{PYTHON_TAG}</PythonTag>
+    <PythonVersion>{PYTHON_VERSION}</PythonVersion>
+
+    <IncludePythonExe Condition="$(IncludePythonExe) == ''">true</IncludePythonExe>
+    <IncludeDistutils Condition="$(IncludeDistutils) == ''">false</IncludeDistutils>
+    <IncludeLib2To3 Condition="$(IncludeLib2To3) == ''">false</IncludeLib2To3>
+    <IncludeVEnv Condition="$(IncludeVEnv) == ''">false</IncludeVEnv>
+
+    <GetPythonRuntimeFilesDependsOn>{PYTHON_TARGET};$(GetPythonRuntimeFilesDependsOn)</GetPythonRuntimeFilesDependsOn>
+  </PropertyGroup>
+
+  <ItemDefinitionGroup Condition="$(Platform) == '{PYTHON_PLATFORM}'">
+    <ClCompile>
+      <AdditionalIncludeDirectories>$(PythonInclude);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+    </ClCompile>
+    <Link>
+      <AdditionalLibraryDirectories>$(PythonLibs);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
+
+  <Target Name="GetPythonRuntimeFiles" Returns="@(PythonRuntime)" DependsOnTargets="$(GetPythonRuntimeFilesDependsOn)" />
+
+  <Target Name="{PYTHON_TARGET}" Returns="@(PythonRuntime)">
+    <ItemGroup>
+      <_PythonRuntimeExe Include="$(PythonHome)\python*.dll" />
+      <_PythonRuntimeExe Include="$(PythonHome)\python*.exe" Condition="$(IncludePythonExe) == 'true'" />
+      <_PythonRuntimeExe>
+        <Link>%(Filename)%(Extension)</Link>
+      </_PythonRuntimeExe>
+      <_PythonRuntimeDlls Include="$(PythonHome)\DLLs\*.pyd" />
+      <_PythonRuntimeDlls Include="$(PythonHome)\DLLs\*.dll" />
+      <_PythonRuntimeDlls>
+        <Link>DLLs\%(Filename)%(Extension)</Link>
+      </_PythonRuntimeDlls>
+      <_PythonRuntimeLib Include="$(PythonHome)\Lib\**\*" Exclude="$(PythonHome)\Lib\**\*.pyc;$(PythonHome)\Lib\site-packages\**\*" />
+      <_PythonRuntimeLib Remove="$(PythonHome)\Lib\distutils\**\*" Condition="$(IncludeDistutils) != 'true'" />
+      <_PythonRuntimeLib Remove="$(PythonHome)\Lib\lib2to3\**\*" Condition="$(IncludeLib2To3) != 'true'" />
+      <_PythonRuntimeLib Remove="$(PythonHome)\Lib\ensurepip\**\*" Condition="$(IncludeVEnv) != 'true'" />
+      <_PythonRuntimeLib Remove="$(PythonHome)\Lib\venv\**\*" Condition="$(IncludeVEnv) != 'true'" />
+      <_PythonRuntimeLib>
+        <Link>Lib\%(RecursiveDir)%(Filename)%(Extension)</Link>
+      </_PythonRuntimeLib>
+      <PythonRuntime Include="@(_PythonRuntimeExe);@(_PythonRuntimeDlls);@(_PythonRuntimeLib)" />
+    </ItemGroup>
+
+    <Message Importance="low" Text="Collected Python runtime from $(PythonHome):%0D%0A@(PythonRuntime->'  %(Link)','%0D%0A')" />
+  </Target>
+</Project>
+"""
+
+
+@public
+def get_props_layout(ns):
+    if ns.include_all or ns.include_props:
+        yield "python.props", ns.temp / "python.props"
+
+
+@public
+def get_props(ns):
+    # TODO: Filter contents of props file according to included/excluded items
+    props = PROPS_TEMPLATE.format_map(PROPS_DATA)
+    return props.encode("utf-8")
index 3da3445..92987af 100644 (file)
@@ -7,6 +7,11 @@
 #include <winuser.h>
 1 RT_MANIFEST "python.manifest"
 
+#if defined(PY_ICON)
+1 ICON DISCARDABLE "icons\python.ico"
+#elif defined(PYW_ICON)
+1 ICON DISCARDABLE "icons\pythonw.ico"
+#else
 1 ICON DISCARDABLE "icons\launcher.ico" 
 2 ICON DISCARDABLE "icons\py.ico" 
 3 ICON DISCARDABLE "icons\pyc.ico" 
@@ -14,6 +19,7 @@
 5 ICON DISCARDABLE "icons\python.ico"
 6 ICON DISCARDABLE "icons\pythonw.ico"
 7 ICON DISCARDABLE "icons\setup.ico" 
+#endif
 
 /////////////////////////////////////////////////////////////////////////////
 //
diff --git a/PC/python_uwp.cpp b/PC/python_uwp.cpp
new file mode 100644 (file)
index 0000000..5c8caa6
--- /dev/null
@@ -0,0 +1,227 @@
+/* Main program when embedded in a UWP application on Windows */
+
+#include "Python.h"
+#include <string.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <shellapi.h>
+
+#include <winrt\Windows.ApplicationModel.h>
+#include <winrt\Windows.Storage.h>
+
+#ifdef PYTHONW
+#ifdef _DEBUG
+const wchar_t *PROGNAME = L"pythonw_d.exe";
+#else
+const wchar_t *PROGNAME = L"pythonw.exe";
+#endif
+#else
+#ifdef _DEBUG
+const wchar_t *PROGNAME = L"python_d.exe";
+#else
+const wchar_t *PROGNAME = L"python.exe";
+#endif
+#endif
+
+static void
+set_user_base()
+{
+    wchar_t envBuffer[2048];
+    try {
+        const auto appData = winrt::Windows::Storage::ApplicationData::Current();
+        if (appData) {
+            const auto localCache = appData.LocalCacheFolder();
+            if (localCache) {
+                auto path = localCache.Path();
+                if (!path.empty() &&
+                    !wcscpy_s(envBuffer, path.c_str()) &&
+                    !wcscat_s(envBuffer, L"\\local-packages")
+                ) {
+                    _wputenv_s(L"PYTHONUSERBASE", envBuffer);
+                }
+            }
+        }
+    } catch (...) {
+    }
+}
+
+static const wchar_t *
+get_argv0(const wchar_t *argv0)
+{
+    winrt::hstring installPath;
+    const wchar_t *launcherPath;
+    wchar_t *buffer;
+    size_t len;
+
+    launcherPath = _wgetenv(L"__PYVENV_LAUNCHER__");
+    if (launcherPath && launcherPath[0]) {
+        len = wcslen(launcherPath) + 1;
+        buffer = (wchar_t *)malloc(sizeof(wchar_t) * len);
+        if (!buffer) {
+            Py_FatalError("out of memory");
+            return NULL;
+        }
+        if (wcscpy_s(buffer, len, launcherPath)) {
+            Py_FatalError("failed to copy to buffer");
+            return NULL;
+        }
+        return buffer;
+    }
+
+    try {
+        const auto package = winrt::Windows::ApplicationModel::Package::Current();
+        if (package) {
+            const auto install = package.InstalledLocation();
+            if (install) {
+                installPath = install.Path();
+            }
+        }
+    }
+    catch (...) {
+    }
+
+    if (!installPath.empty()) {
+        len = installPath.size() + wcslen(PROGNAME) + 2;
+    } else {
+        len = wcslen(argv0) + wcslen(PROGNAME) + 1;
+    }
+
+    buffer = (wchar_t *)malloc(sizeof(wchar_t) * len);
+    if (!buffer) {
+        Py_FatalError("out of memory");
+        return NULL;
+    }
+
+    if (!installPath.empty()) {
+        if (wcscpy_s(buffer, len, installPath.c_str())) {
+            Py_FatalError("failed to copy to buffer");
+            return NULL;
+        }
+        if (wcscat_s(buffer, len, L"\\")) {
+            Py_FatalError("failed to concatenate backslash");
+            return NULL;
+        }
+    } else {
+        if (wcscpy_s(buffer, len, argv0)) {
+            Py_FatalError("failed to copy argv[0]");
+            return NULL;
+        }
+
+        wchar_t *name = wcsrchr(buffer, L'\\');
+        if (name) {
+            name[1] = L'\0';
+        } else {
+            buffer[0] = L'\0';
+        }
+    }
+
+    if (wcscat_s(buffer, len, PROGNAME)) {
+        Py_FatalError("failed to concatenate program name");
+        return NULL;
+    }
+
+    return buffer;
+}
+
+static wchar_t *
+get_process_name()
+{
+    DWORD bufferLen = MAX_PATH;
+    DWORD len = bufferLen;
+    wchar_t *r = NULL;
+
+    while (!r) {
+        r = (wchar_t *)malloc(bufferLen * sizeof(wchar_t));
+        if (!r) {
+            Py_FatalError("out of memory");
+            return NULL;
+        }
+        len = GetModuleFileNameW(NULL, r, bufferLen);
+        if (len == 0) {
+            free((void *)r);
+            return NULL;
+        } else if (len == bufferLen &&
+                   GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+            free(r);
+            r = NULL;
+            bufferLen *= 2;
+        }
+    }
+
+    return r;
+}
+
+int
+wmain(int argc, wchar_t **argv)
+{
+    const wchar_t **new_argv;
+    int new_argc;
+    const wchar_t *exeName;
+
+    new_argc = argc;
+    new_argv = (const wchar_t**)malloc(sizeof(wchar_t *) * (argc + 2));
+    if (new_argv == NULL) {
+        Py_FatalError("out of memory");
+        return -1;
+    }
+
+    exeName = get_process_name();
+
+    new_argv[0] = get_argv0(exeName ? exeName : argv[0]);
+    for (int i = 1; i < argc; ++i) {
+        new_argv[i] = argv[i];
+    }
+
+    set_user_base();
+
+    if (exeName) {
+        const wchar_t *p = wcsrchr(exeName, L'\\');
+        if (p) {
+            const wchar_t *moduleName = NULL;
+            if (*p++ == L'\\') {
+                if (wcsnicmp(p, L"pip", 3) == 0) {
+                    moduleName = L"pip";
+                    _wputenv_s(L"PIP_USER", L"true");
+                }
+                else if (wcsnicmp(p, L"idle", 4) == 0) {
+                    moduleName = L"idlelib";
+                }
+            }
+
+            if (moduleName) {
+                new_argc += 2;
+                for (int i = argc; i >= 1; --i) {
+                    new_argv[i + 2] = new_argv[i];
+                }
+                new_argv[1] = L"-m";
+                new_argv[2] = moduleName;
+            }
+        }
+    }
+
+    /* Override program_full_path from here so that
+       sys.executable is set correctly. */
+    _Py_SetProgramFullPath(new_argv[0]);
+
+    int result = Py_Main(new_argc, (wchar_t **)new_argv);
+
+    free((void *)exeName);
+    free((void *)new_argv);
+
+    return result;
+}
+
+#ifdef PYTHONW
+
+int WINAPI wWinMain(
+    HINSTANCE hInstance,      /* handle to current instance */
+    HINSTANCE hPrevInstance,  /* handle to previous instance */
+    LPWSTR lpCmdLine,         /* pointer to command line */
+    int nCmdShow              /* show state of window */
+)
+{
+    return wmain(__argc, __wargv);
+}
+
+#endif
diff --git a/PC/store_info.txt b/PC/store_info.txt
new file mode 100644 (file)
index 0000000..e252692
--- /dev/null
@@ -0,0 +1,146 @@
+# Overview
+
+NOTE: This file requires more content.
+
+Since Python 3.7.2, releases have been made through the Microsoft Store
+to allow easy installation on Windows 10.0.17763.0 and later.
+
+# Building
+
+To build the store package, the PC/layout script should be used.
+Execute the directory with the build of Python to package, and pass
+"-h" for full command-line options.
+
+To sideload test builds, you will need a local certificate.
+Instructions are available at
+https://docs.microsoft.com/windows/uwp/packaging/create-certificate-package-signing.
+
+After exporting your certificate, you will need the subject name and
+SHA256 hash. The `certutil -dump <cert file>` command will display this
+information.
+
+To build for sideloading, use these commands in PowerShell:
+
+```
+$env:APPX_DATA_PUBLISHER=<your certificate subject name>
+$env:APPX_DATA_SHA256=<your certificate SHA256>
+$env:SigningCertificateFile=<your certificate file>
+
+python PC/layout --copy <layout directory> --include-appxmanifest
+Tools/msi/make_appx.ps1 <layout directory> python.msix -sign
+
+Add-AppxPackage python.msix
+```
+
+(Note that only the last command requires PowerShell, and the others
+can be used from Command Prompt. You can also double-click to install
+the final package.)
+
+To build for publishing to the Store, use these commands:
+
+```
+$env:APPX_DATA_PUBLISHER = $null
+$env:APPX_DATA_SHA256 = $null
+
+python PC/layout --copy <layout directory> --preset-appxmanifest --precompile
+Tools/msi/make_appx.ps1 <layout directory> python.msix
+```
+
+Note that this package cannot be installed locally. It may only be
+added to a submission for the store.
+
+
+# Submission Metadata
+
+This file contains the text that we use to fill out the store listing
+for the Microsoft Store. It needs to be entered manually when creating
+a new submission via the dashboard at
+https://partner.microsoft.com/dashboard.
+
+We keep it here for convenience and to allow it to be updated via pull
+requests.
+
+## Title
+
+Python 3.7
+
+## Short Title
+
+Python
+
+## Description
+
+Python is an easy to learn, powerful programming language. It has efficient high-level data structures and a simple but effective approach to object-oriented programming. Python’s elegant syntax and dynamic typing, together with its interpreted nature, make it an ideal language for scripting and rapid application development in many areas on most platforms.
+
+The Python interpreter and the extensive standard library are freely available in source or binary form for all major platforms from the Python Web site, https://www.python.org/, and may be freely distributed. The same site also contains distributions of and pointers to many free third party Python modules, programs and tools, and additional documentation.
+
+The Python interpreter is easily extended with new functions and data types implemented in C or C++ (or other languages callable from C). Python is also suitable as an extension language for customizable applications.
+
+## ShortDescription
+
+The Python 3.7 interpreter and runtime.
+
+## Copyright Trademark Information
+
+(c) Python Software Foundation
+
+## Additional License Terms
+
+Visit https://docs.python.org/3.7/license.html for latest license terms.
+
+PSF LICENSE AGREEMENT FOR PYTHON 3.7
+
+1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and
+   the Individual or Organization ("Licensee") accessing and otherwise using Python
+   3.7 software in source or binary form and its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, PSF hereby
+   grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
+   analyze, test, perform and/or display publicly, prepare derivative works,
+   distribute, and otherwise use Python 3.7 alone or in any derivative
+   version, provided, however, that PSF's License Agreement and PSF's notice of
+   copyright, i.e., "Copyright © 2001-2018 Python Software Foundation; All Rights
+   Reserved" are retained in Python 3.7 alone or in any derivative version
+   prepared by Licensee.
+
+3. In the event Licensee prepares a derivative work that is based on or
+   incorporates Python 3.7 or any part thereof, and wants to make the
+   derivative work available to others as provided herein, then Licensee hereby
+   agrees to include in any such work a brief summary of the changes made to Python
+   3.7.
+
+4. PSF is making Python 3.7 available to Licensee on an "AS IS" basis.
+   PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.  BY WAY OF
+   EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR
+   WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE
+   USE OF PYTHON 3.7 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.7
+   FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF
+   MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.7, OR ANY DERIVATIVE
+   THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material breach of
+   its terms and conditions.
+
+7. Nothing in this License Agreement shall be deemed to create any relationship
+   of agency, partnership, or joint venture between PSF and Licensee.  This License
+   Agreement does not grant permission to use PSF trademarks or trade name in a
+   trademark sense to endorse or promote products or services of Licensee, or any
+   third party.
+
+8. By copying, installing or otherwise using Python 3.7, Licensee agrees
+   to be bound by the terms and conditions of this License Agreement.
+
+## Features
+
+* Easy to install Python runtime
+* Supported by core CPython team
+* Find Python, Pip and Idle on PATH
+
+## Search Terms
+
+* Python
+* Scripting
+* Interpreter
+
index ddaf3b1..6f31066 100644 (file)
@@ -757,9 +757,13 @@ Reg2Py(BYTE *retDataBuf, DWORD retDataSize, DWORD typ)
                         PyMem_Free(str);
                         return NULL;
                     }
-                    PyList_SetItem(obData,
-                                   index,
-                                   PyUnicode_FromWideChar(str[index], len));
+                    PyObject *uni = PyUnicode_FromWideChar(str[index], len);
+                    if (uni == NULL) {
+                        Py_DECREF(obData);
+                        PyMem_Free(str);
+                        return NULL;
+                    }
+                    PyList_SET_ITEM(obData, index, uni);
                 }
                 PyMem_Free(str);
 
diff --git a/PCbuild/_distutils_findvs.vcxproj.filters b/PCbuild/_distutils_findvs.vcxproj.filters
deleted file mode 100644 (file)
index 757c14e..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>\r
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
-  <ItemGroup>\r
-    <ResourceCompile Include="..\PC\python_nt.rc" />\r
-  </ItemGroup>\r
-  <ItemGroup>\r
-    <Filter Include="Source Files">\r
-      <UniqueIdentifier>{c56a5dd3-7838-48e9-a781-855d8be7370f}</UniqueIdentifier>\r
-    </Filter>\r
-  </ItemGroup>\r
-  <ItemGroup>\r
-    <ClCompile Include="..\PC\_findvs.cpp">\r
-      <Filter>Source Files</Filter>\r
-    </ClCompile>\r
-  </ItemGroup>\r
-</Project>
\ No newline at end of file
index 417f5c8..51287f3 100644 (file)
   <Target Name="_CleanTclTkDLL" BeforeTargets="Clean">\r
     <Delete Files="@(_TclTkDLL->'$(OutDir)%(Filename)%(Extension)')" />\r
   </Target>\r
+  <Target Name="_WriteTCL_LIBRARY" Outputs="$(OutDir)TCL_LIBRARY.env" AfterTargets="Build">\r
+    <WriteLinesToFile File="$(OutDir)TCL_LIBRARY.env" Lines="$(tcltkdir)\lib\tcl$(TclMajorVersion).$(TclMinorVersion)" Encoding="utf-8" Overwrite="true" />\r
+  </Target>\r
+  <Target Name="_CleanTCL_LIBRARY" BeforeTargets="Clean">\r
+    <Delete Files="$(OutDir)TCL_LIBRARY.env" />\r
+  </Target>\r
 </Project>
\ No newline at end of file
index 885dddc..88f7fc4 100644 (file)
@@ -148,4 +148,5 @@ goto :eof
 \r
 :Version\r
 rem Display the current build version information\r
-%MSBUILD% "%dir%python.props" /t:ShowVersionInfo /v:m /nologo %1 %2 %3 %4 %5 %6 %7 %8 %9\r
+call "%dir%find_msbuild.bat" %MSBUILD%\r
+if not ERRORLEVEL 1 %MSBUILD% "%dir%pythoncore.vcxproj" /t:ShowVersionInfo /v:m /nologo %1 %2 %3 %4 %5 %6 %7 %8 %9\r
index 1bbb39b..0cd2a5d 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 and later provide vswhere.exe, which can be used\r
+@if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" goto :skip_vswhere\r
+@set _Py_MSBuild_Root=\r
+@for /F "tokens=*" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -property installationPath -latest') DO @(set _Py_MSBuild_Root=%%i\MSBuild)\r
+@if not defined _Py_MSBuild_Root goto :skip_vswhere\r
+@for %%j in (Current 15.0) DO @if exist "%_Py_MSBuild_Root%\%%j\Bin\msbuild.exe" (set MSBUILD="%_Py_MSBuild_Root%\%%j\Bin\msbuild.exe")\r
+@set _Py_MSBuild_Root=\r
+@if defined MSBUILD @if exist %MSBUILD% (set _Py_MSBuild_Source=Visual Studio installation) & goto :found\r
+:skip_vswhere\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
index 55e8fa6..dfbc924 100644 (file)
@@ -49,7 +49,7 @@ echo.Fetching external libraries...
 \r
 set libraries=\r
 set libraries=%libraries%                                       bzip2-1.0.6\r
-if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries%     openssl-1.1.0i\r
+if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries%     openssl-1.1.0j\r
 set libraries=%libraries%                                       sqlite-3.21.0.0\r
 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.8.0\r
 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.8.0\r
@@ -72,7 +72,7 @@ for %%e in (%libraries%) do (
 echo.Fetching external binaries...\r
 \r
 set binaries=\r
-if NOT "%IncludeSSL%"=="false"     set binaries=%binaries% openssl-bin-1.1.0i\r
+if NOT "%IncludeSSL%"=="false"     set binaries=%binaries% openssl-bin-1.1.0j\r
 if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.8.0\r
 if NOT "%IncludeSSLSrc%"=="false"  set binaries=%binaries% nasm-2.11.06\r
 \r
index 2056897..19d8d2f 100644 (file)
@@ -9,6 +9,7 @@
     <IncludeTests Condition="'$(IncludeTest)' == ''">true</IncludeTests>\r
     <IncludeSSL Condition="'$(IncludeSSL)' == ''">true</IncludeSSL>\r
     <IncludeTkinter Condition="'$(IncludeTkinter)' == ''">true</IncludeTkinter>\r
+    <IncludeUwp Condition="'$(IncludeUwp)' == ''">false</IncludeUwp>\r
   </PropertyGroup>\r
 \r
   <ItemDefinitionGroup>\r
     <!-- pyshellext.dll -->\r
     <Projects Include="pyshellext.vcxproj" />\r
     <!-- Extension modules -->\r
-    <ExtensionModules Include="_asyncio;_contextvars;_ctypes;_decimal;_distutils_findvs;_elementtree;_msi;_multiprocessing;_overlapped;pyexpat;_queue;select;unicodedata;winsound" />\r
+    <ExtensionModules Include="_asyncio;_contextvars;_ctypes;_decimal;_elementtree;_msi;_multiprocessing;_overlapped;pyexpat;_queue;select;unicodedata;winsound" />\r
     <!-- Extension modules that require external sources -->\r
     <ExternalModules Include="_bz2;_lzma;_sqlite3" />\r
+    <!-- venv launchers -->\r
+    <Projects Include="venvlauncher.vcxproj;venvwlauncher.vcxproj" />\r
     <!-- _ssl will build _socket as well, which may cause conflicts in parallel builds -->\r
     <ExtensionModules Include="_socket" Condition="!$(IncludeSSL) or !$(IncludeExternals)" />\r
     <ExternalModules Include="_ssl;_hashlib" Condition="$(IncludeSSL)" />\r
@@ -70,6 +73,9 @@
     <Projects2 Include="_freeze_importlib.vcxproj" />\r
     <!-- python[w].exe -->\r
     <Projects2 Include="python.vcxproj;pythonw.vcxproj" />\r
+    <Projects2 Include="python_uwp.vcxproj;pythonw_uwp.vcxproj" Condition="$(IncludeUwp)" />\r
+    <!-- venv[w]launcher.exe -->\r
+    <Projects2 Include="venvlauncher.vcxproj;venvwlauncher.vcxproj" />\r
   </ItemGroup>\r
 \r
   <Target Name="Build">\r
index dd3dd4c..554bd13 100644 (file)
@@ -93,7 +93,13 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_queue", "_queue.vcxproj",
 EndProject\r
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblzma", "liblzma.vcxproj", "{12728250-16EC-4DC6-94D7-E21DD88947F8}"\r
 EndProject\r
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_distutils_findvs", "_distutils_findvs.vcxproj", "{41ADEDF9-11D8-474E-B4D7-BB82332C878E}"\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "python_uwp", "python_uwp.vcxproj", "{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}"\r
+EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "venvlauncher", "venvlauncher.vcxproj", "{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}"\r
+EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "venvwlauncher", "venvwlauncher.vcxproj", "{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}"\r
+EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pythonw_uwp", "pythonw_uwp.vcxproj", "{AB603547-1E2A-45B3-9E09-B04596006393}"\r
 EndProject\r
 Global\r
        GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
@@ -695,22 +701,70 @@ Global
                {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|Win32.Build.0 = Release|Win32\r
                {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|x64.ActiveCfg = Release|x64\r
                {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|x64.Build.0 = Release|x64\r
-               {41ADEDF9-11D8-474E-B4D7-BB82332C878E}.Debug|Win32.ActiveCfg = Debug|Win32\r
-               {41ADEDF9-11D8-474E-B4D7-BB82332C878E}.Debug|Win32.Build.0 = Debug|Win32\r
-               {41ADEDF9-11D8-474E-B4D7-BB82332C878E}.Debug|x64.ActiveCfg = Debug|x64\r
-               {41ADEDF9-11D8-474E-B4D7-BB82332C878E}.Debug|x64.Build.0 = Debug|x64\r
-               {41ADEDF9-11D8-474E-B4D7-BB82332C878E}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32\r
-               {41ADEDF9-11D8-474E-B4D7-BB82332C878E}.PGInstrument|Win32.Build.0 = PGInstrument|Win32\r
-               {41ADEDF9-11D8-474E-B4D7-BB82332C878E}.PGInstrument|x64.ActiveCfg = PGInstrument|x64\r
-               {41ADEDF9-11D8-474E-B4D7-BB82332C878E}.PGInstrument|x64.Build.0 = PGInstrument|x64\r
-               {41ADEDF9-11D8-474E-B4D7-BB82332C878E}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32\r
-               {41ADEDF9-11D8-474E-B4D7-BB82332C878E}.PGUpdate|Win32.Build.0 = PGUpdate|Win32\r
-               {41ADEDF9-11D8-474E-B4D7-BB82332C878E}.PGUpdate|x64.ActiveCfg = PGUpdate|x64\r
-               {41ADEDF9-11D8-474E-B4D7-BB82332C878E}.PGUpdate|x64.Build.0 = PGUpdate|x64\r
-               {41ADEDF9-11D8-474E-B4D7-BB82332C878E}.Release|Win32.ActiveCfg = Release|Win32\r
-               {41ADEDF9-11D8-474E-B4D7-BB82332C878E}.Release|Win32.Build.0 = Release|Win32\r
-               {41ADEDF9-11D8-474E-B4D7-BB82332C878E}.Release|x64.ActiveCfg = Release|x64\r
-               {41ADEDF9-11D8-474E-B4D7-BB82332C878E}.Release|x64.Build.0 = Release|x64\r
+               {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|Win32.ActiveCfg = Debug|Win32\r
+               {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|Win32.Build.0 = Debug|Win32\r
+               {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|x64.ActiveCfg = Debug|x64\r
+               {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Debug|x64.Build.0 = Debug|x64\r
+               {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32\r
+               {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|Win32.Build.0 = PGInstrument|Win32\r
+               {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|x64.ActiveCfg = PGInstrument|x64\r
+               {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGInstrument|x64.Build.0 = PGInstrument|x64\r
+               {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32\r
+               {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|Win32.Build.0 = PGUpdate|Win32\r
+               {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|x64.ActiveCfg = PGUpdate|x64\r
+               {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.PGUpdate|x64.Build.0 = PGUpdate|x64\r
+               {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|Win32.ActiveCfg = Release|Win32\r
+               {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|Win32.Build.0 = Release|Win32\r
+               {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|x64.ActiveCfg = Release|x64\r
+               {9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}.Release|x64.Build.0 = Release|x64\r
+               {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|Win32.ActiveCfg = Debug|Win32\r
+               {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|Win32.Build.0 = Debug|Win32\r
+               {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|x64.ActiveCfg = Debug|x64\r
+               {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Debug|x64.Build.0 = Debug|x64\r
+               {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32\r
+               {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|Win32.Build.0 = PGInstrument|Win32\r
+               {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|x64.ActiveCfg = PGInstrument|x64\r
+               {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGInstrument|x64.Build.0 = PGInstrument|x64\r
+               {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32\r
+               {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|Win32.Build.0 = PGUpdate|Win32\r
+               {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|x64.ActiveCfg = PGUpdate|x64\r
+               {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.PGUpdate|x64.Build.0 = PGUpdate|x64\r
+               {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|Win32.ActiveCfg = Release|Win32\r
+               {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|Win32.Build.0 = Release|Win32\r
+               {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|x64.ActiveCfg = Release|x64\r
+               {494BAC80-A60C-43A9-99E7-ACB691CE2C4D}.Release|x64.Build.0 = Release|x64\r
+               {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|Win32.ActiveCfg = Debug|Win32\r
+               {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|Win32.Build.0 = Debug|Win32\r
+               {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|x64.ActiveCfg = Debug|x64\r
+               {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Debug|x64.Build.0 = Debug|x64\r
+               {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32\r
+               {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|Win32.Build.0 = PGInstrument|Win32\r
+               {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|x64.ActiveCfg = PGInstrument|x64\r
+               {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGInstrument|x64.Build.0 = PGInstrument|x64\r
+               {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32\r
+               {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|Win32.Build.0 = PGUpdate|Win32\r
+               {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|x64.ActiveCfg = PGUpdate|x64\r
+               {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.PGUpdate|x64.Build.0 = PGUpdate|x64\r
+               {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|Win32.ActiveCfg = Release|Win32\r
+               {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|Win32.Build.0 = Release|Win32\r
+               {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|x64.ActiveCfg = Release|x64\r
+               {FDB84CBB-2FB6-47C8-A2D6-091E0833239D}.Release|x64.Build.0 = Release|x64\r
+               {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|Win32.ActiveCfg = Debug|Win32\r
+               {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|Win32.Build.0 = Debug|Win32\r
+               {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|x64.ActiveCfg = Debug|x64\r
+               {AB603547-1E2A-45B3-9E09-B04596006393}.Debug|x64.Build.0 = Debug|x64\r
+               {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32\r
+               {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|Win32.Build.0 = PGInstrument|Win32\r
+               {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|x64.ActiveCfg = PGInstrument|x64\r
+               {AB603547-1E2A-45B3-9E09-B04596006393}.PGInstrument|x64.Build.0 = PGInstrument|x64\r
+               {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32\r
+               {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|Win32.Build.0 = PGUpdate|Win32\r
+               {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|x64.ActiveCfg = PGUpdate|x64\r
+               {AB603547-1E2A-45B3-9E09-B04596006393}.PGUpdate|x64.Build.0 = PGUpdate|x64\r
+               {AB603547-1E2A-45B3-9E09-B04596006393}.Release|Win32.ActiveCfg = Release|Win32\r
+               {AB603547-1E2A-45B3-9E09-B04596006393}.Release|Win32.Build.0 = Release|Win32\r
+               {AB603547-1E2A-45B3-9E09-B04596006393}.Release|x64.ActiveCfg = Release|x64\r
+               {AB603547-1E2A-45B3-9E09-B04596006393}.Release|x64.Build.0 = Release|x64\r
        EndGlobalSection\r
        GlobalSection(SolutionProperties) = preSolution\r
                HideSolutionNode = FALSE\r
index ec90080..0a0c181 100644 (file)
@@ -23,8 +23,6 @@ setlocal
 if "%PCBUILD%"=="" (set PCBUILD=%~dp0)\r
 if "%EXTERNALS_DIR%"=="" (set EXTERNALS_DIR=%PCBUILD%\..\externals)\r
 \r
-set OUT=\r
-set SRC=\r
 set ORG_SETTING=\r
 \r
 :CheckOpts\r
@@ -32,19 +30,12 @@ if "%~1"=="-h" shift & goto Usage
 if "%~1"=="--certificate" (set SigningCertificate=%~2) && shift && shift & goto CheckOpts\r
 if "%~1"=="-c" (set SigningCertificate=%~2) && shift && shift & goto CheckOpts\r
 if "%~1"=="--organization" (set ORG_SETTING=--organization "%~2") && shift && shift && goto CheckOpts\r
-if "%~1"=="-i" (SET SRC=$~2) && shift && shift && goto CheckOpts\r
-if "%~1"=="--in" (SET SRC=$~2) && shift && shift && goto CheckOpts\r
-if "%~1"=="-o" (set OUT=$~2) && shift && shift && goto CheckOpts\r
-if "%~1"=="--out" (set OUT=$~2) && shift && shift && goto CheckOpts\r
 \r
 if "%~1"=="" goto Build\r
 echo Unrecognized option: %1\r
 goto Usage\r
 \r
 :Build\r
-if not defined SRC (echo --in directory is required & exit /b 1)\r
-if not defined OUT (echo --out directory is required & exit /b 1)\r
-\r
 call "%PCBUILD%\find_msbuild.bat" %MSBUILD%\r
 if ERRORLEVEL 1 (echo Cannot locate MSBuild.exe on PATH or as MSBUILD variable & exit /b 2)\r
 \r
index 1aca6ea..2b36835 100644 (file)
       <FileName Required="true" />\r
     </ParameterGroup>\r
     <Task>\r
-      <Code Type="Fragment" Language="cs">\r
+      <Using Namespace="System.Diagnostics"/>\r
+      <Using Namespace="System.IO"/>\r
+      <Using Namespace="System.Runtime.InteropServices"/>\r
+      <Using Namespace="System.Text"/>\r
+      <Code Type="Method" Language="cs">\r
 <![CDATA[\r
-string fullPath = System.IO.Path.GetFullPath(FileName);\r
-Log.LogMessage("Looking for " + fullPath, MessageImportance.Normal);\r
-foreach (System.Diagnostics.Process p in System.Diagnostics.Process.GetProcesses()) {\r
-    try {\r
-        Log.LogMessage("Found running process: " + p.MainModule.FileName, MessageImportance.Low);\r
-        if (fullPath.Equals(System.IO.Path.GetFullPath(p.MainModule.FileName), StringComparison.OrdinalIgnoreCase)) {\r
-            Log.LogMessage("Terminating " + p.MainModule.FileName, MessageImportance.High);\r
-            p.Kill();\r
+[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)]\r
+public static extern bool QueryFullProcessImageName([In]IntPtr hProcess, [In]int dwFlags,\r
+                                                    [Out]StringBuilder lpExeName, ref int lpdwSize);\r
+public override bool Execute() {\r
+    string fullPath = Path.GetFullPath(FileName);\r
+    Log.LogMessage("Looking for " + fullPath, MessageImportance.Normal);\r
+    foreach (Process p in Process.GetProcesses()) {\r
+        try {\r
+            int pathLength = 32768;\r
+            StringBuilder pathBuilder = new StringBuilder(pathLength);\r
+            if (QueryFullProcessImageName(p.Handle, 0, pathBuilder, ref pathLength)) {\r
+                string exeName = Path.GetFullPath(pathBuilder.ToString());\r
+                Log.LogMessage("Found running process: " + exeName, MessageImportance.Low);\r
+                if (fullPath.Equals(exeName, StringComparison.OrdinalIgnoreCase)) {\r
+                    Log.LogMessage("Terminating " + exeName, MessageImportance.High);\r
+                    p.Kill();\r
+                }\r
+            }\r
+        } catch {\r
         }\r
-    } catch {\r
     }\r
+    return true;\r
 }\r
 ]]>\r
       </Code>\r
index 2040a0b..3e60cdd 100644 (file)
@@ -49,8 +49,8 @@
     <sqlite3Dir>$(ExternalsDir)sqlite-3.21.0.0\</sqlite3Dir>\r
     <bz2Dir>$(ExternalsDir)bzip2-1.0.6\</bz2Dir>\r
     <lzmaDir>$(ExternalsDir)xz-5.2.2\</lzmaDir>\r
-    <opensslDir>$(ExternalsDir)openssl-1.1.0i\</opensslDir>\r
-    <opensslOutDir>$(ExternalsDir)openssl-bin-1.1.0i\$(ArchName)\</opensslOutDir>\r
+    <opensslDir>$(ExternalsDir)openssl-1.1.0j\</opensslDir>\r
+    <opensslOutDir>$(ExternalsDir)openssl-bin-1.1.0j\$(ArchName)\</opensslOutDir>\r
     <opensslIncludeDir>$(opensslOutDir)include</opensslIncludeDir>\r
     <nasmDir>$(ExternalsDir)\nasm-2.11.06\</nasmDir>\r
     <zlibDir>$(ExternalsDir)\zlib-1.2.11\</zlibDir>\r
     -->\r
     <_RegistryVersion>$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0@ProductVersion)</_RegistryVersion>\r
     <_RegistryVersion Condition="$(_RegistryVersion) == ''">$(Registry:HKEY_LOCAL_MACHINE\WOW6432Node\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0@ProductVersion)</_RegistryVersion>\r
-    <DefaultWindowsSDKVersion>10.0.17134.0</DefaultWindowsSDKVersion>\r
-    <DefaultWindowsSDKVersion Condition="$(_RegistryVersion) == '10.0.16299'">10.0.16299.0</DefaultWindowsSDKVersion>\r
-    <DefaultWindowsSDKVersion Condition="$(_RegistryVersion) == '10.0.15063'">10.0.15063.0</DefaultWindowsSDKVersion>\r
-    <DefaultWindowsSDKVersion Condition="$(_RegistryVersion) == '10.0.14393'">10.0.14393.0</DefaultWindowsSDKVersion>\r
-    <DefaultWindowsSDKVersion Condition="$(_RegistryVersion) == '10.0.10586'">10.0.10586.0</DefaultWindowsSDKVersion>\r
-    <DefaultWindowsSDKVersion Condition="$(_RegistryVersion) == '10.0.10240'">10.0.10240.0</DefaultWindowsSDKVersion>\r
+    <!-- Sometimes the version in the registry has to .0 suffix, and sometimes it doesn't. Check and add it -->\r
+    <_RegistryVersion Condition="$(_RegistryVersion) != '' and !$(_RegistryVersion.EndsWith('.0'))">$(_RegistryVersion).0</_RegistryVersion>\r
+\r
+    <!-- The minimum allowed SDK version to use for building -->\r
+    <DefaultWindowsSDKVersion>10.0.10586.0</DefaultWindowsSDKVersion>\r
+    <DefaultWindowsSDKVersion Condition="$([System.Version]::Parse($(_RegistryVersion))) > $([System.Version]::Parse($(DefaultWindowsSDKVersion)))">$(_RegistryVersion)</DefaultWindowsSDKVersion>\r
   </PropertyGroup>\r
   \r
+  <PropertyGroup Condition="$(WindowsTargetPlatformVersion) == ''">\r
+    <WindowsTargetPlatformVersion>$(DefaultWindowsSDKVersion)</WindowsTargetPlatformVersion>\r
+  </PropertyGroup>\r
+\r
   <PropertyGroup Condition="'$(OverrideVersion)' == ''">\r
     <!--\r
     Read version information from Include\patchlevel.h. The following properties are set:\r
     <Message Importance="high" Text="Field3Value:         $(Field3Value)" />\r
     <Message Importance="high" Text="SysWinVer:           $(SysWinVer)" />\r
     <Message Importance="high" Text="PyDllName:           $(PyDllName)" />\r
+    <Message Importance="high" Text="WindowsSdkVersion:   $(TargetPlatformVersion)" />\r
   </Target>\r
 </Project>\r
similarity index 76%
rename from PCbuild/_distutils_findvs.vcxproj
rename to PCbuild/python_uwp.vcxproj
index c4dde92..7b3842a 100644 (file)
     </ProjectConfiguration>\r
   </ItemGroup>\r
   <PropertyGroup Label="Globals">\r
-    <ProjectGuid>{41ADEDF9-11D8-474E-B4D7-BB82332C878E}</ProjectGuid>\r
-    <RootNamespace>_distutils_findvs</RootNamespace>\r
-    <Keyword>Win32Proj</Keyword>\r
+    <ProjectGuid>{9DE9E23D-C8D4-4817-92A9-920A8B1FE5FF}</ProjectGuid>\r
   </PropertyGroup>\r
   <Import Project="python.props" />\r
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />\r
   <PropertyGroup Label="Configuration">\r
-    <ConfigurationType>DynamicLibrary</ConfigurationType>\r
-    <CharacterSet>NotSet</CharacterSet>\r
+    <ConfigurationType>Application</ConfigurationType>\r
+    <UseOfMfc>false</UseOfMfc>\r
+    <CharacterSet>Unicode</CharacterSet>\r
   </PropertyGroup>\r
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />\r
-  <PropertyGroup>\r
-    <TargetExt>.pyd</TargetExt>\r
-  </PropertyGroup>\r
   <ImportGroup Label="ExtensionSettings">\r
   </ImportGroup>\r
   <ImportGroup Label="PropertySheets">\r
     <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>\r
   </PropertyGroup>\r
   <ItemDefinitionGroup>\r
+    <ClCompile>\r
+      <PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+      <AdditionalOptions>/EHsc /std:c++17 %(AdditionalOptions)</AdditionalOptions>\r
+    </ClCompile>\r
     <Link>\r
-      <AdditionalDependencies>version.lib;ole32.lib;oleaut32.lib;Microsoft.VisualStudio.Setup.Configuration.Native.lib;%(AdditionalDependencies)</AdditionalDependencies>\r
-      <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories);$(PySourcePath)PC\external\$(PlatformToolset)\$(ArchName)</AdditionalLibraryDirectories>\r
+      <AdditionalDependencies>windowsapp.lib;%(AdditionalDependencies)</AdditionalDependencies>\r
+      <SubSystem>Console</SubSystem>\r
     </Link>\r
   </ItemDefinitionGroup>\r
   <ItemGroup>\r
-    <ClCompile Include="..\PC\_findvs.cpp" />\r
+    <None Include="..\PC\pycon.ico" />\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <ResourceCompile Include="..\PC\python_exe.rc" />\r
   </ItemGroup>\r
   <ItemGroup>\r
-    <ResourceCompile Include="..\PC\python_nt.rc" />\r
+    <ClCompile Include="..\PC\python_uwp.cpp" />\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ProjectReference Include="pythoncore.vcxproj">\r
index acc17df..a10a521 100644 (file)
   <Target Name="_WarnAboutZlib" BeforeTargets="PrepareForBuild" Condition="!$(IncludeExternals)">\r
     <Warning Text="Not including zlib is not a supported configuration." />\r
   </Target>\r
+\r
+  <PropertyGroup>\r
+    <VCRedistDir>$(VCInstallDir)\Redist\MSVC\$(VCToolsRedistVersion)\</VCRedistDir>\r
+    <VCRedistDir Condition="$(Platform) == 'Win32'">$(VCRedistDir)x86\</VCRedistDir>\r
+    <VCRedistDir Condition="$(Platform) != 'Win32'">$(VCRedistDir)$(Platform)\</VCRedistDir>\r
+  </PropertyGroup>\r
+  <ItemGroup Condition="$(VCInstallDir) != ''">\r
+    <VCRuntimeDLL Include="$(VCRedistDir)\**\vcruntime*.dll" />\r
+  </ItemGroup>\r
+  <Target Name="_CopyVCRuntime" AfterTargets="Build" Inputs="@(VCRuntimeDLL)" Outputs="$(OutDir)%(Filename)%(Extension)">\r
+    <Copy SourceFiles="%(VCRuntimeDLL.FullPath)" DestinationFolder="$(OutDir)" />\r
+  </Target>\r
+  <Target Name="_CleanVCRuntime" AfterTargets="Clean">\r
+    <Delete Files="@(VCRuntimeDLL->'$(OutDir)%(Filename)%(Extension)')" />\r
+  </Target>\r
 </Project>\r
diff --git a/PCbuild/pythonw_uwp.vcxproj b/PCbuild/pythonw_uwp.vcxproj
new file mode 100644 (file)
index 0000000..06943f9
--- /dev/null
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+  <ItemGroup Label="ProjectConfigurations">\r
+    <ProjectConfiguration Include="Debug|Win32">\r
+      <Configuration>Debug</Configuration>\r
+      <Platform>Win32</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Debug|x64">\r
+      <Configuration>Debug</Configuration>\r
+      <Platform>x64</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="PGInstrument|Win32">\r
+      <Configuration>PGInstrument</Configuration>\r
+      <Platform>Win32</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="PGInstrument|x64">\r
+      <Configuration>PGInstrument</Configuration>\r
+      <Platform>x64</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="PGUpdate|Win32">\r
+      <Configuration>PGUpdate</Configuration>\r
+      <Platform>Win32</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="PGUpdate|x64">\r
+      <Configuration>PGUpdate</Configuration>\r
+      <Platform>x64</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Release|Win32">\r
+      <Configuration>Release</Configuration>\r
+      <Platform>Win32</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Release|x64">\r
+      <Configuration>Release</Configuration>\r
+      <Platform>x64</Platform>\r
+    </ProjectConfiguration>\r
+  </ItemGroup>\r
+  <PropertyGroup Label="Globals">\r
+    <ProjectGuid>{AB603547-1E2A-45B3-9E09-B04596006393}</ProjectGuid>\r
+  </PropertyGroup>\r
+  <Import Project="python.props" />\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />\r
+  <PropertyGroup Label="Configuration">\r
+    <ConfigurationType>Application</ConfigurationType>\r
+    <UseOfMfc>false</UseOfMfc>\r
+    <CharacterSet>Unicode</CharacterSet>\r
+  </PropertyGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />\r
+  <ImportGroup Label="ExtensionSettings">\r
+  </ImportGroup>\r
+  <ImportGroup Label="PropertySheets">\r
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+    <Import Project="pyproject.props" />\r
+  </ImportGroup>\r
+  <PropertyGroup Label="UserMacros" />\r
+  <PropertyGroup>\r
+    <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>\r
+  </PropertyGroup>\r
+  <ItemDefinitionGroup>\r
+    <ClCompile>\r
+      <PreprocessorDefinitions>PYTHONW;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+      <AdditionalOptions>/EHsc /std:c++17 %(AdditionalOptions)</AdditionalOptions>\r
+    </ClCompile>\r
+    <Link>\r
+      <AdditionalDependencies>windowsapp.lib;%(AdditionalDependencies)</AdditionalDependencies>\r
+      <SubSystem>Windows</SubSystem>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+  <ItemGroup>\r
+    <None Include="..\PC\pyconw.ico" />\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <ResourceCompile Include="..\PC\pythonw_exe.rc" />\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <ClCompile Include="..\PC\python_uwp.cpp" />\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <ProjectReference Include="pythoncore.vcxproj">\r
+      <Project>{cf7ac3d1-e2df-41d2-bea6-1e2556cdea26}</Project>\r
+      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>\r
+    </ProjectReference>\r
+  </ItemGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r
+  <ImportGroup Label="ExtensionTargets">\r
+  </ImportGroup>\r
+</Project>\r
diff --git a/PCbuild/venvlauncher.vcxproj b/PCbuild/venvlauncher.vcxproj
new file mode 100644 (file)
index 0000000..b10607c
--- /dev/null
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+  <ItemGroup Label="ProjectConfigurations">\r
+    <ProjectConfiguration Include="Debug|Win32">\r
+      <Configuration>Debug</Configuration>\r
+      <Platform>Win32</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Debug|x64">\r
+      <Configuration>Debug</Configuration>\r
+      <Platform>x64</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="PGInstrument|Win32">\r
+      <Configuration>PGInstrument</Configuration>\r
+      <Platform>Win32</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="PGInstrument|x64">\r
+      <Configuration>PGInstrument</Configuration>\r
+      <Platform>x64</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="PGUpdate|Win32">\r
+      <Configuration>PGUpdate</Configuration>\r
+      <Platform>Win32</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="PGUpdate|x64">\r
+      <Configuration>PGUpdate</Configuration>\r
+      <Platform>x64</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Release|Win32">\r
+      <Configuration>Release</Configuration>\r
+      <Platform>Win32</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Release|x64">\r
+      <Configuration>Release</Configuration>\r
+      <Platform>x64</Platform>\r
+    </ProjectConfiguration>\r
+  </ItemGroup>\r
+  <PropertyGroup Label="Globals">\r
+    <ProjectGuid>{494BAC80-A60C-43A9-99E7-ACB691CE2C4D}</ProjectGuid>\r
+    <RootNamespace>venvlauncher</RootNamespace>\r
+    <TargetName>venvlauncher</TargetName>\r
+    <SupportPGO>false</SupportPGO>\r
+  </PropertyGroup>\r
+  <Import Project="python.props" />\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />\r
+  <PropertyGroup Label="Configuration">\r
+    <ConfigurationType>Application</ConfigurationType>\r
+    <CharacterSet>MultiByte</CharacterSet>\r
+  </PropertyGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />\r
+  <ImportGroup Label="ExtensionSettings">\r
+  </ImportGroup>\r
+  <PropertyGroup>\r
+    <MakeVersionInfoBeforeTarget>ClCompile</MakeVersionInfoBeforeTarget>\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
+    <Import Project="pyproject.props" />\r
+  </ImportGroup>\r
+  <PropertyGroup Label="UserMacros" />\r
+  <ItemDefinitionGroup>\r
+    <ClCompile>\r
+      <PreprocessorDefinitions>_CONSOLE;VENV_REDIRECT;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\r
+    </ClCompile>\r
+    <ResourceCompile>\r
+      <PreprocessorDefinitions>PY_ICON;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+    </ResourceCompile>\r
+    <Link>\r
+      <AdditionalDependencies>version.lib;%(AdditionalDependencies)</AdditionalDependencies>\r
+      <SubSystem>Console</SubSystem>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+  <ItemGroup>\r
+    <ClCompile Include="..\PC\launcher.c" />\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <None Include="..\PC\launcher.ico" />\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <ResourceCompile Include="..\PC\pylauncher.rc" />\r
+  </ItemGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r
+  <ImportGroup Label="ExtensionTargets">\r
+  </ImportGroup>\r
+</Project>\r
diff --git a/PCbuild/venvwlauncher.vcxproj b/PCbuild/venvwlauncher.vcxproj
new file mode 100644 (file)
index 0000000..7115ce1
--- /dev/null
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+  <ItemGroup Label="ProjectConfigurations">\r
+    <ProjectConfiguration Include="Debug|Win32">\r
+      <Configuration>Debug</Configuration>\r
+      <Platform>Win32</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Debug|x64">\r
+      <Configuration>Debug</Configuration>\r
+      <Platform>x64</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="PGInstrument|Win32">\r
+      <Configuration>PGInstrument</Configuration>\r
+      <Platform>Win32</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="PGInstrument|x64">\r
+      <Configuration>PGInstrument</Configuration>\r
+      <Platform>x64</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="PGUpdate|Win32">\r
+      <Configuration>PGUpdate</Configuration>\r
+      <Platform>Win32</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="PGUpdate|x64">\r
+      <Configuration>PGUpdate</Configuration>\r
+      <Platform>x64</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Release|Win32">\r
+      <Configuration>Release</Configuration>\r
+      <Platform>Win32</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Release|x64">\r
+      <Configuration>Release</Configuration>\r
+      <Platform>x64</Platform>\r
+    </ProjectConfiguration>\r
+  </ItemGroup>\r
+  <PropertyGroup Label="Globals">\r
+    <ProjectGuid>{FDB84CBB-2FB6-47C8-A2D6-091E0833239D}</ProjectGuid>\r
+    <RootNamespace>venvwlauncher</RootNamespace>\r
+    <TargetName>venvwlauncher</TargetName>\r
+    <SupportPGO>false</SupportPGO>\r
+  </PropertyGroup>\r
+  <Import Project="python.props" />\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />\r
+  <PropertyGroup Label="Configuration">\r
+    <ConfigurationType>Application</ConfigurationType>\r
+    <CharacterSet>MultiByte</CharacterSet>\r
+  </PropertyGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />\r
+  <ImportGroup Label="ExtensionSettings">\r
+  </ImportGroup>\r
+  <PropertyGroup>\r
+    <MakeVersionInfoBeforeTarget>ClCompile</MakeVersionInfoBeforeTarget>\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
+    <Import Project="pyproject.props" />\r
+  </ImportGroup>\r
+  <PropertyGroup Label="UserMacros" />\r
+  <ItemDefinitionGroup>\r
+    <ClCompile>\r
+      <PreprocessorDefinitions>_WINDOWS;VENV_REDIRECT;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\r
+    </ClCompile>\r
+    <ResourceCompile>\r
+      <PreprocessorDefinitions>PYW_ICON;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+    </ResourceCompile>\r
+    <Link>\r
+      <AdditionalDependencies>version.lib;%(AdditionalDependencies)</AdditionalDependencies>\r
+      <SubSystem>Windows</SubSystem>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+  <ItemGroup>\r
+    <ClCompile Include="..\PC\launcher.c" />\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <None Include="..\PC\launcher.ico" />\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <ResourceCompile Include="..\PC\pylauncher.rc" />\r
+  </ItemGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r
+  <ImportGroup Label="ExtensionTargets">\r
+  </ImportGroup>\r
+</Project>\r
index edb291a..58dc0f7 100644 (file)
@@ -153,20 +153,37 @@ _PyOS_WindowsConsoleReadline(HANDLE hStdIn)
             wbuf = (wchar_t*)PyMem_RawMalloc(wbuflen * sizeof(wchar_t));
             if (wbuf)
                 wcscpy_s(wbuf, wbuflen, wbuf_local);
+            else {
+                PyErr_NoMemory();
+                goto exit;
+            }
+        }
+        else {
+            wchar_t *tmp = PyMem_RawRealloc(wbuf, wbuflen * sizeof(wchar_t));
+            if (tmp == NULL) {
+                PyErr_NoMemory();
+                goto exit;
+            }
+            wbuf = tmp;
         }
-        else
-            wbuf = (wchar_t*)PyMem_RawRealloc(wbuf, wbuflen * sizeof(wchar_t));
     }
 
     if (wbuf[0] == '\x1a') {
         buf = PyMem_RawMalloc(1);
         if (buf)
             buf[0] = '\0';
+        else {
+            PyErr_NoMemory();
+        }
         goto exit;
     }
 
     u8len = WideCharToMultiByte(CP_UTF8, 0, wbuf, total_read, NULL, 0, NULL, NULL);
     buf = PyMem_RawMalloc(u8len + 1);
+    if (buf == NULL) {
+        PyErr_NoMemory();
+        goto exit;
+    }
     u8len = WideCharToMultiByte(CP_UTF8, 0, wbuf, total_read, buf, u8len, NULL, NULL);
     buf[u8len] = '\0';
 
@@ -211,8 +228,12 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
                     int wlen;
                     wlen = MultiByteToWideChar(CP_UTF8, 0, prompt, -1,
                             NULL, 0);
-                    if (wlen &&
-                        (wbuf = PyMem_RawMalloc(wlen * sizeof(wchar_t)))) {
+                    if (wlen) {
+                        wbuf = PyMem_RawMalloc(wlen * sizeof(wchar_t));
+                        if (wbuf == NULL) {
+                            PyErr_NoMemory();
+                            return NULL;
+                        }
                         wlen = MultiByteToWideChar(CP_UTF8, 0, prompt, -1,
                                 wbuf, wlen);
                         if (wlen) {
@@ -236,8 +257,10 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
 
     n = 100;
     p = (char *)PyMem_RawMalloc(n);
-    if (p == NULL)
+    if (p == NULL) {
+        PyErr_NoMemory();
         return NULL;
+    }
 
     fflush(sys_stdout);
     if (prompt)
@@ -314,6 +337,10 @@ PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
 
     if (_PyOS_ReadlineLock == NULL) {
         _PyOS_ReadlineLock = PyThread_allocate_lock();
+        if (_PyOS_ReadlineLock == NULL) {
+            PyErr_SetString(PyExc_MemoryError, "can't allocate lock");
+            return NULL;
+        }
     }
 
     _PyOS_ReadlineTState = PyThreadState_GET();
@@ -341,8 +368,12 @@ PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
 
     len = strlen(rv) + 1;
     res = PyMem_Malloc(len);
-    if (res != NULL)
+    if (res != NULL) {
         memcpy(res, rv, len);
+    }
+    else {
+        PyErr_NoMemory();
+    }
     PyMem_RawFree(rv);
 
     return res;
index fbc9888..1a36f1c 100644 (file)
@@ -953,6 +953,11 @@ tok_nextc(struct tok_state *tok)
                 buflen = PyBytes_GET_SIZE(u);
                 buf = PyBytes_AS_STRING(u);
                 newtok = PyMem_MALLOC(buflen+1);
+                if (newtok == NULL) {
+                    Py_DECREF(u);
+                    tok->done = E_NOMEM;
+                    return EOF;
+                }
                 strcpy(newtok, buf);
                 Py_DECREF(u);
             }
index 6c35f95..20b853e 100644 (file)
@@ -293,92 +293,77 @@ static int test_initialize_pymain(void)
 }
 
 
-static void
-dump_config(void)
+static int
+dump_config_impl(void)
 {
-#define ASSERT_EQUAL(a, b) \
-    if ((a) != (b)) { \
-        printf("ERROR: %s != %s (%i != %i)\n", #a, #b, (a), (b)); \
-        exit(1); \
+    PyObject *config = NULL;
+    PyObject *dict = NULL;
+
+    config = PyDict_New();
+    if (config == NULL) {
+        goto error;
     }
-#define ASSERT_STR_EQUAL(a, b) \
-    if ((a) == NULL || (b == NULL) || wcscmp((a), (b)) != 0) { \
-        printf("ERROR: %s != %s ('%ls' != '%ls')\n", #a, #b, (a), (b)); \
-        exit(1); \
+
+    /* global config */
+    dict = _Py_GetGlobalVariablesAsDict();
+    if (dict == NULL) {
+        goto error;
     }
+    if (PyDict_SetItemString(config, "global_config", dict) < 0) {
+        goto error;
+    }
+    Py_CLEAR(dict);
 
+    /* core config */
     PyInterpreterState *interp = PyThreadState_Get()->interp;
-    _PyCoreConfig *config = &interp->core_config;
-
-    printf("install_signal_handlers = %i\n", config->install_signal_handlers);
-
-    printf("Py_IgnoreEnvironmentFlag = %i\n", Py_IgnoreEnvironmentFlag);
+    const _PyCoreConfig *core_config = &interp->core_config;
+    dict = _PyCoreConfig_AsDict(core_config);
+    if (dict == NULL) {
+        goto error;
+    }
+    if (PyDict_SetItemString(config, "core_config", dict) < 0) {
+        goto error;
+    }
+    Py_CLEAR(dict);
 
-    printf("use_hash_seed = %i\n", config->use_hash_seed);
-    printf("hash_seed = %lu\n", config->hash_seed);
+    /* main config */
+    const _PyMainInterpreterConfig *main_config = &interp->config;
+    dict = _PyMainInterpreterConfig_AsDict(main_config);
+    if (dict == NULL) {
+        goto error;
+    }
+    if (PyDict_SetItemString(config, "main_config", dict) < 0) {
+        goto error;
+    }
+    Py_CLEAR(dict);
+
+    PyObject *json = PyImport_ImportModule("json");
+    PyObject *res = PyObject_CallMethod(json, "dumps", "O", config);
+    Py_DECREF(json);
+    Py_CLEAR(config);
+    if (res == NULL) {
+        goto error;
+    }
 
-    printf("allocator = %s\n", config->allocator);
+    PySys_FormatStdout("%S\n", res);
+    Py_DECREF(res);
 
-    printf("dev_mode = %i\n", config->dev_mode);
-    printf("faulthandler = %i\n", config->faulthandler);
-    printf("tracemalloc = %i\n", config->tracemalloc);
-    printf("import_time = %i\n", config->import_time);
-    printf("show_ref_count = %i\n", config->show_ref_count);
-    printf("show_alloc_count = %i\n", config->show_alloc_count);
-    printf("dump_refs = %i\n", config->dump_refs);
-    printf("malloc_stats = %i\n", config->malloc_stats);
+    return 0;
 
-    printf("coerce_c_locale = %i\n", config->coerce_c_locale);
-    printf("coerce_c_locale_warn = %i\n", config->coerce_c_locale_warn);
-    printf("utf8_mode = %i\n", config->utf8_mode);
+error:
+    Py_XDECREF(config);
+    Py_XDECREF(dict);
+    return -1;
+}
 
-    printf("program_name = %ls\n", config->program_name);
-    ASSERT_STR_EQUAL(config->program_name, Py_GetProgramName());
 
-    printf("argc = %i\n", config->argc);
-    printf("argv = [");
-    for (int i=0; i < config->argc; i++) {
-        if (i) {
-            printf(", ");
-        }
-        printf("\"%ls\"", config->argv[i]);
+static void
+dump_config(void)
+{
+    if (dump_config_impl() < 0) {
+        fprintf(stderr, "failed to dump the configuration:\n");
+        PyErr_Print();
     }
-    printf("]\n");
-
-    printf("program = %ls\n", config->program);
-    /* FIXME: test xoptions */
-    /* FIXME: test warnoptions */
-    /* FIXME: test module_search_path_env */
-    /* FIXME: test home */
-    /* FIXME: test module_search_paths */
-    /* FIXME: test executable */
-    /* FIXME: test prefix */
-    /* FIXME: test base_prefix */
-    /* FIXME: test exec_prefix */
-    /* FIXME: test base_exec_prefix */
-    /* FIXME: test dll_path */
-
-    printf("Py_IsolatedFlag = %i\n", Py_IsolatedFlag);
-    printf("Py_NoSiteFlag = %i\n", Py_NoSiteFlag);
-    printf("Py_BytesWarningFlag = %i\n", Py_BytesWarningFlag);
-    printf("Py_InspectFlag = %i\n", Py_InspectFlag);
-    printf("Py_InteractiveFlag = %i\n", Py_InteractiveFlag);
-    printf("Py_OptimizeFlag = %i\n", Py_OptimizeFlag);
-    printf("Py_DebugFlag = %i\n", Py_DebugFlag);
-    printf("Py_DontWriteBytecodeFlag = %i\n", Py_DontWriteBytecodeFlag);
-    printf("Py_VerboseFlag = %i\n", Py_VerboseFlag);
-    printf("Py_QuietFlag = %i\n", Py_QuietFlag);
-    printf("Py_NoUserSiteDirectory = %i\n", Py_NoUserSiteDirectory);
-    printf("Py_UnbufferedStdioFlag = %i\n", Py_UnbufferedStdioFlag);
-    /* FIXME: test legacy_windows_fs_encoding */
-    /* FIXME: test legacy_windows_stdio */
-
-    printf("_disable_importlib = %i\n", config->_disable_importlib);
-    /* cannot test _Py_CheckHashBasedPycsMode: the symbol is not exported */
-    printf("Py_FrozenFlag = %i\n", Py_FrozenFlag);
-
-#undef ASSERT_EQUAL
-#undef ASSERT_STR_EQUAL
 }
 
 
@@ -482,10 +467,30 @@ static int test_init_from_config(void)
     Py_SetProgramName(L"./globalvar");
     config.program_name = L"./conf_program_name";
 
-    /* FIXME: test argc/argv */
+    static wchar_t* argv[2] = {
+        L"-c",
+        L"pass",
+    };
+    config.argc = Py_ARRAY_LENGTH(argv);
+    config.argv = argv;
+
     config.program = L"conf_program";
-    /* FIXME: test xoptions */
-    /* FIXME: test warnoptions */
+
+    static wchar_t* xoptions[3] = {
+        L"core_xoption1=3",
+        L"core_xoption2=",
+        L"core_xoption3",
+    };
+    config.nxoption = Py_ARRAY_LENGTH(xoptions);
+    config.xoptions = xoptions;
+
+    static wchar_t* warnoptions[2] = {
+        L"default",
+        L"error::ResourceWarning",
+    };
+    config.nwarnoption = Py_ARRAY_LENGTH(warnoptions);
+    config.warnoptions = warnoptions;
+
     /* FIXME: test module_search_path_env */
     /* FIXME: test home */
     /* FIXME: test path config: module_search_path .. dll_path */
index 29e475d..80dca65 100644 (file)
@@ -256,7 +256,11 @@ already_warned(PyObject *registry, PyObject *key, int should_set)
     version_obj = _PyDict_GetItemId(registry, &PyId_version);
     if (version_obj == NULL
         || !PyLong_CheckExact(version_obj)
-        || PyLong_AsLong(version_obj) != _PyRuntime.warnings.filters_version) {
+        || PyLong_AsLong(version_obj) != _PyRuntime.warnings.filters_version)
+    {
+        if (PyErr_Occurred()) {
+            return -1;
+        }
         PyDict_Clear(registry);
         version_obj = PyLong_FromLong(_PyRuntime.warnings.filters_version);
         if (version_obj == NULL)
index 07227c2..1e182c7 100644 (file)
@@ -4092,6 +4092,9 @@ parsenumber(struct compiling *c, const char *s)
     }
     /* Create a duplicate without underscores. */
     dup = PyMem_Malloc(strlen(s) + 1);
+    if (dup == NULL) {
+        return PyErr_NoMemory();
+    }
     end = dup;
     for (; *s; s++) {
         if (*s != '_') {
@@ -4326,8 +4329,10 @@ fstring_compile_expr(const char *expr_start, const char *expr_end,
     len = expr_end - expr_start;
     /* Allocate 3 extra bytes: open paren, close paren, null byte. */
     str = PyMem_RawMalloc(len + 3);
-    if (str == NULL)
+    if (str == NULL) {
+        PyErr_NoMemory();
         return NULL;
+    }
 
     str[0] = '(';
     memcpy(str+1, expr_start, len);
index 073b2ea..e2afba2 100644 (file)
 #  endif
 #endif
 
+#ifdef _Py_MEMORY_SANITIZER
+#  include <sanitizer/msan_interface.h>
+#endif
+
 #ifdef Py_DEBUG
 int _Py_HashSecret_Initialized = 0;
 #else
@@ -143,6 +147,11 @@ py_getrandom(void *buffer, Py_ssize_t size, int blocking, int raise)
         else {
             n = syscall(SYS_getrandom, dest, n, flags);
         }
+#  ifdef _Py_MEMORY_SANITIZER
+        if (n > 0) {
+             __msan_unpoison(dest, n);
+        }
+#  endif
 #endif
 
         if (n < 0) {
index 0d1519b..c394b9b 100644 (file)
@@ -4419,6 +4419,20 @@ PyEval_GetBuiltins(void)
         return current_frame->f_builtins;
 }
 
+/* Convenience function to get a builtin from its name */
+PyObject *
+_PyEval_GetBuiltinId(_Py_Identifier *name)
+{
+    PyObject *attr = _PyDict_GetItemIdWithError(PyEval_GetBuiltins(), name);
+    if (attr) {
+        Py_INCREF(attr);
+    }
+    else if (!PyErr_Occurred()) {
+        PyErr_SetObject(PyExc_AttributeError, _PyUnicode_FromId(name));
+    }
+    return attr;
+}
+
 PyObject *
 PyEval_GetLocals(void)
 {
@@ -5027,7 +5041,7 @@ unicode_concatenate(PyObject *v, PyObject *w,
             PyObject *names = f->f_code->co_names;
             PyObject *name = GETITEM(names, oparg);
             PyObject *locals = f->f_locals;
-            if (PyDict_CheckExact(locals) &&
+            if (locals && PyDict_CheckExact(locals) &&
                 PyDict_GetItem(locals, name) == v) {
                 if (PyDict_DelItem(locals, name) != 0) {
                     PyErr_Clear();
@@ -5056,7 +5070,7 @@ getarray(long a[256])
             Py_DECREF(l);
             return NULL;
         }
-        PyList_SetItem(l, i, x);
+        PyList_SET_ITEM(l, i, x);
     }
     for (i = 0; i < 256; i++)
         a[i] = 0;
@@ -5078,7 +5092,7 @@ _Py_GetDXProfile(PyObject *self, PyObject *args)
             Py_DECREF(l);
             return NULL;
         }
-        PyList_SetItem(l, i, x);
+        PyList_SET_ITEM(l, i, x);
     }
     return l;
 #endif
index eb3cd35..c315ae3 100644 (file)
@@ -130,8 +130,10 @@ PyObject *_PyCodec_Lookup(const char *encoding)
 
     /* Next, scan the search functions in order of registration */
     args = PyTuple_New(1);
-    if (args == NULL)
-        goto onError;
+    if (args == NULL) {
+        Py_DECREF(v);
+        return NULL;
+    }
     PyTuple_SET_ITEM(args,0,v);
 
     len = PyList_Size(interp->codec_search_path);
index ba64903..90c71e3 100644 (file)
@@ -1105,14 +1105,14 @@ error:
 }
 
 static PyObject *
-token_get_var(PyContextToken *self)
+token_get_var(PyContextToken *self, void *Py_UNUSED(ignored))
 {
     Py_INCREF(self->tok_var);
     return (PyObject *)self->tok_var;
 }
 
 static PyObject *
-token_get_old_value(PyContextToken *self)
+token_get_old_value(PyContextToken *self, void *Py_UNUSED(ignored))
 {
     if (self->tok_oldval == NULL) {
         return get_token_missing();
index b413f4e..5e71d37 100644 (file)
@@ -180,6 +180,24 @@ error:
     return 1;
 }
 
+
+int
+_Py_GetForceASCII(void)
+{
+    if (force_ascii == -1) {
+        force_ascii = check_force_ascii();
+    }
+    return force_ascii;
+}
+
+
+void
+_Py_ResetForceASCII(void)
+{
+    force_ascii = -1;
+}
+
+
 static int
 encode_ascii(const wchar_t *text, char **str,
              size_t *error_pos, const char **reason,
@@ -234,6 +252,18 @@ encode_ascii(const wchar_t *text, char **str,
     *str = result;
     return 0;
 }
+#else
+int
+_Py_GetForceASCII(void)
+{
+    return 0;
+}
+
+void
+_Py_ResetForceASCII(void)
+{
+    /* nothing to do */
+}
 #endif   /* !defined(__APPLE__) && !defined(__ANDROID__) && !defined(MS_WINDOWS) */
 
 
@@ -1363,18 +1393,9 @@ _Py_read(int fd, void *buf, size_t count)
      * handler raised an exception. */
     assert(!PyErr_Occurred());
 
-#ifdef MS_WINDOWS
-    if (count > INT_MAX) {
-        /* On Windows, the count parameter of read() is an int */
-        count = INT_MAX;
-    }
-#else
-    if (count > PY_SSIZE_T_MAX) {
-        /* if count is greater than PY_SSIZE_T_MAX,
-         * read() result is undefined */
-        count = PY_SSIZE_T_MAX;
+    if (count > _PY_READ_MAX) {
+        count = _PY_READ_MAX;
     }
-#endif
 
     _Py_BEGIN_SUPPRESS_IPH
     do {
@@ -1425,15 +1446,10 @@ _Py_write_impl(int fd, const void *buf, size_t count, int gil_held)
            depending on heap usage). */
         count = 32767;
     }
-    else if (count > INT_MAX)
-        count = INT_MAX;
-#else
-    if (count > PY_SSIZE_T_MAX) {
-        /* write() should truncate count to PY_SSIZE_T_MAX, but it's safer
-         * to do it ourself to have a portable behaviour. */
-        count = PY_SSIZE_T_MAX;
-    }
 #endif
+    if (count > _PY_WRITE_MAX) {
+        count = _PY_WRITE_MAX;
+    }
 
     if (gil_held) {
         do {
@@ -1797,7 +1813,7 @@ _Py_GetLocaleconvNumeric(PyObject **decimal_point, PyObject **thousands_sep,
     if (change_locale) {
         oldloc = setlocale(LC_CTYPE, NULL);
         if (!oldloc) {
-            PyErr_SetString(PyExc_RuntimeWarning, "faild to get LC_CTYPE locale");
+            PyErr_SetString(PyExc_RuntimeWarning, "failed to get LC_CTYPE locale");
             return -1;
         }
 
@@ -1813,7 +1829,7 @@ _Py_GetLocaleconvNumeric(PyObject **decimal_point, PyObject **thousands_sep,
         }
 
         if (loc != NULL) {
-            /* Only set the locale temporarilty the LC_CTYPE locale
+            /* Only set the locale temporarily the LC_CTYPE locale
                if LC_NUMERIC locale is different than LC_CTYPE locale and
                decimal_point and/or thousands_sep are non-ASCII or longer than
                1 byte */
index ba09cc6..ef81d15 100644 (file)
@@ -396,9 +396,10 @@ typedef struct {
     PyObject *decimal_point;
     PyObject *thousands_sep;
     const char *grouping;
+    char *grouping_buffer;
 } LocaleInfo;
 
-#define STATIC_LOCALE_INFO_INIT {0, 0, 0}
+#define STATIC_LOCALE_INFO_INIT {0, 0, 0, 0}
 
 /* describes the layout for an integer, see the comment in
    calc_number_widths() for details */
@@ -460,7 +461,8 @@ parse_number(PyObject *s, Py_ssize_t pos, Py_ssize_t end,
 /* not all fields of format are used.  for example, precision is
    unused.  should this take discrete params in order to be more clear
    about what it does?  or is passing a single format parameter easier
-   and more efficient enough to justify a little obfuscation? */
+   and more efficient enough to justify a little obfuscation?
+   Return -1 on error. */
 static Py_ssize_t
 calc_number_widths(NumberFieldWidths *spec, Py_ssize_t n_prefix,
                    Py_UCS4 sign_char, PyObject *number, Py_ssize_t n_start,
@@ -539,9 +541,12 @@ calc_number_widths(NumberFieldWidths *spec, Py_ssize_t n_prefix,
         Py_UCS4 grouping_maxchar;
         spec->n_grouped_digits = _PyUnicode_InsertThousandsGrouping(
             NULL, 0,
-            0, NULL,
-            spec->n_digits, spec->n_min_width,
+            NULL, 0, spec->n_digits,
+            spec->n_min_width,
             locale->grouping, locale->thousands_sep, &grouping_maxchar);
+        if (spec->n_grouped_digits == -1) {
+            return -1;
+        }
         *maxchar = Py_MAX(*maxchar, grouping_maxchar);
     }
 
@@ -633,26 +638,14 @@ fill_number(_PyUnicodeWriter *writer, const NumberFieldWidths *spec,
     /* Only for type 'c' special case, it has no digits. */
     if (spec->n_digits != 0) {
         /* Fill the digits with InsertThousandsGrouping. */
-        char *pdigits;
-        if (PyUnicode_READY(digits))
-            return -1;
-        pdigits = PyUnicode_DATA(digits);
-        if (PyUnicode_KIND(digits) < kind) {
-            pdigits = _PyUnicode_AsKind(digits, kind);
-            if (pdigits == NULL)
-                return -1;
-        }
         r = _PyUnicode_InsertThousandsGrouping(
-                writer->buffer, writer->pos,
-                spec->n_grouped_digits,
-                pdigits + kind * d_pos,
-                spec->n_digits, spec->n_min_width,
+                writer, spec->n_grouped_digits,
+                digits, d_pos, spec->n_digits,
+                spec->n_min_width,
                 locale->grouping, locale->thousands_sep, NULL);
         if (r == -1)
             return -1;
         assert(r == spec->n_grouped_digits);
-        if (PyUnicode_KIND(digits) < kind)
-            PyMem_Free(pdigits);
         d_pos += spec->n_digits;
     }
     if (toupper) {
@@ -705,11 +698,22 @@ get_locale_info(enum LocaleType type, LocaleInfo *locale_info)
 {
     switch (type) {
     case LT_CURRENT_LOCALE: {
+        const char *grouping;
         if (_Py_GetLocaleconvNumeric(&locale_info->decimal_point,
                                      &locale_info->thousands_sep,
-                                     &locale_info->grouping) < 0) {
+                                     &grouping) < 0) {
+            return -1;
+        }
+
+        /* localeconv() grouping can become a dangling pointer or point
+           to a different string if another thread calls localeconv() during
+           the string formatting. Copy the string to avoid this risk. */
+        locale_info->grouping_buffer = _PyMem_Strdup(grouping);
+        if (locale_info->grouping_buffer == NULL) {
+            PyErr_NoMemory();
             return -1;
         }
+        locale_info->grouping = locale_info->grouping_buffer;
         break;
     }
     case LT_DEFAULT_LOCALE:
@@ -743,6 +747,7 @@ free_locale_info(LocaleInfo *locale_info)
 {
     Py_XDECREF(locale_info->decimal_point);
     Py_XDECREF(locale_info->thousands_sep);
+    PyMem_Free(locale_info->grouping_buffer);
 }
 
 /************************************************************************/
@@ -980,6 +985,9 @@ format_long_internal(PyObject *value, const InternalFormatSpec *format,
     n_total = calc_number_widths(&spec, n_prefix, sign_char, tmp, inumeric_chars,
                                  inumeric_chars + n_digits, n_remainder, 0,
                                  &locale, format, &maxchar);
+    if (n_total == -1) {
+        goto done;
+    }
 
     /* Allocate the memory. */
     if (_PyUnicodeWriter_Prepare(writer, n_total, maxchar) == -1)
@@ -1125,6 +1133,9 @@ format_float_internal(PyObject *value,
     n_total = calc_number_widths(&spec, 0, sign_char, unicode_tmp, index,
                                  index + n_digits, n_remainder, has_decimal,
                                  &locale, format, &maxchar);
+    if (n_total == -1) {
+        goto done;
+    }
 
     /* Allocate the memory. */
     if (_PyUnicodeWriter_Prepare(writer, n_total, maxchar) == -1)
@@ -1308,6 +1319,9 @@ format_complex_internal(PyObject *value,
                                     i_re, i_re + n_re_digits, n_re_remainder,
                                     re_has_decimal, &locale, &tmp_format,
                                     &maxchar);
+    if (n_re_total == -1) {
+        goto done;
+    }
 
     /* Same formatting, but always include a sign, unless the real part is
      * going to be omitted, in which case we use whatever sign convention was
@@ -1318,6 +1332,9 @@ format_complex_internal(PyObject *value,
                                     i_im, i_im + n_im_digits, n_im_remainder,
                                     im_has_decimal, &locale, &tmp_format,
                                     &maxchar);
+    if (n_im_total == -1) {
+        goto done;
+    }
 
     if (skip_re)
         n_re_total = 0;
index 5d17947..5f5d135 100644 (file)
@@ -833,7 +833,7 @@ remove_module(PyObject *name)
         if (!PyMapping_HasKey(modules, name)) {
             return;
         }
-        Py_FatalError("import:  deleting existing key in"
+        Py_FatalError("import:  deleting existing key in "
                       "sys.modules failed");
     }
 }
@@ -2138,10 +2138,10 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file)
     }
 
     mod = _PyImport_FindExtensionObject(name, path);
-    if (mod != NULL) {
+    if (mod != NULL || PyErr_Occurred()) {
         Py_DECREF(name);
         Py_DECREF(path);
-        Py_INCREF(mod);
+        Py_XINCREF(mod);
         return mod;
     }
 
index 6d06266..7d60614 100644 (file)
@@ -689,11 +689,12 @@ r_string(Py_ssize_t n, RFILE *p)
         p->buf_size = n;
     }
     else if (p->buf_size < n) {
-        p->buf = PyMem_REALLOC(p->buf, n);
-        if (p->buf == NULL) {
+        char *tmp = PyMem_REALLOC(p->buf, n);
+        if (tmp == NULL) {
             PyErr_NoMemory();
             return NULL;
         }
+        p->buf = tmp;
         p->buf_size = n;
     }
 
index 07aa01c..aacc5f5 100644 (file)
@@ -205,6 +205,27 @@ Py_SetProgramName(const wchar_t *program_name)
 }
 
 
+void
+_Py_SetProgramFullPath(const wchar_t *program_full_path)
+{
+    if (program_full_path == NULL || program_full_path[0] == L'\0') {
+        return;
+    }
+
+    PyMemAllocatorEx old_alloc;
+    _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
+
+    PyMem_RawFree(_Py_path_config.program_full_path);
+    _Py_path_config.program_full_path = _PyMem_RawWcsdup(program_full_path);
+
+    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
+
+    if (_Py_path_config.program_full_path == NULL) {
+        Py_FatalError("Py_SetProgramFullPath() failed: out of memory");
+    }
+}
+
+
 wchar_t *
 Py_GetPath(void)
 {
index 76a0edb..a3b078f 100644 (file)
@@ -50,9 +50,9 @@ lastn_const_start(const _Py_CODEUNIT *codestr, Py_ssize_t i, Py_ssize_t n)
 
 /* Scans through EXTENDED ARGs, seeking the index of the effective opcode */
 static Py_ssize_t
-find_op(const _Py_CODEUNIT *codestr, Py_ssize_t i)
+find_op(const _Py_CODEUNIT *codestr, Py_ssize_t codelen, Py_ssize_t i)
 {
-    while (_Py_OPCODE(codestr[i]) == EXTENDED_ARG) {
+    while (i < codelen && _Py_OPCODE(codestr[i]) == EXTENDED_ARG) {
         i++;
     }
     return i;
@@ -128,8 +128,9 @@ copy_op_arg(_Py_CODEUNIT *codestr, Py_ssize_t i, unsigned char op,
    Called with codestr pointing to the first LOAD_CONST.
 */
 static Py_ssize_t
-fold_tuple_on_constants(_Py_CODEUNIT *codestr, Py_ssize_t c_start,
-                        Py_ssize_t opcode_end, PyObject *consts, int n)
+fold_tuple_on_constants(_Py_CODEUNIT *codestr, Py_ssize_t codelen,
+                        Py_ssize_t c_start, Py_ssize_t opcode_end,
+                        PyObject *consts, int n)
 {
     /* Pre-conditions */
     assert(PyList_CheckExact(consts));
@@ -142,7 +143,7 @@ fold_tuple_on_constants(_Py_CODEUNIT *codestr, Py_ssize_t c_start,
 
     for (Py_ssize_t i = 0, pos = c_start; i < n; i++, pos++) {
         assert(pos < opcode_end);
-        pos = find_op(codestr, pos);
+        pos = find_op(codestr, codelen, pos);
         assert(_Py_OPCODE(codestr[pos]) == LOAD_CONST);
 
         unsigned int arg = get_arg(codestr, pos);
@@ -267,7 +268,7 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
         goto exitError;
     assert(PyList_Check(consts));
 
-    for (i=find_op(codestr, 0) ; i<codelen ; i=nexti) {
+    for (i=find_op(codestr, codelen, 0) ; i<codelen ; i=nexti) {
         opcode = _Py_OPCODE(codestr[i]);
         op_start = i;
         while (op_start >= 1 && _Py_OPCODE(codestr[op_start-1]) == EXTENDED_ARG) {
@@ -305,7 +306,8 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
                 if (j > 0 && lastlc >= j) {
                     h = lastn_const_start(codestr, op_start, j);
                     if (ISBASICBLOCK(blocks, h, op_start)) {
-                        h = fold_tuple_on_constants(codestr, h, i+1, consts, j);
+                        h = fold_tuple_on_constants(codestr, codelen,
+                                                    h, i+1, consts, j);
                         break;
                     }
                 }
@@ -342,7 +344,7 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
             case JUMP_IF_FALSE_OR_POP:
             case JUMP_IF_TRUE_OR_POP:
                 h = get_arg(codestr, i) / sizeof(_Py_CODEUNIT);
-                tgt = find_op(codestr, h);
+                tgt = find_op(codestr, codelen, h);
 
                 j = _Py_OPCODE(codestr[tgt]);
                 if (CONDITIONAL_JUMP(j)) {
@@ -383,7 +385,7 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
             case SETUP_WITH:
             case SETUP_ASYNC_WITH:
                 h = GETJUMPTGT(codestr, i);
-                tgt = find_op(codestr, h);
+                tgt = find_op(codestr, codelen, h);
                 /* Replace JUMP_* to a RETURN into just a RETURN */
                 if (UNCONDITIONAL_JUMP(opcode) &&
                     _Py_OPCODE(codestr[tgt]) == RETURN_VALUE) {
@@ -412,7 +414,7 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
                 }
                 if (h > i + 1) {
                     fill_nops(codestr, i + 1, h);
-                    nexti = find_op(codestr, h);
+                    nexti = find_op(codestr, codelen, h);
                 }
                 break;
         }
index ba4b548..94b6d43 100644 (file)
@@ -523,6 +523,7 @@ done:
 char *
 _Py_SetLocaleFromEnv(int category)
 {
+    char *res;
 #ifdef __ANDROID__
     const char *locale;
     const char **pvar;
@@ -569,10 +570,12 @@ _Py_SetLocaleFromEnv(int category)
         }
     }
 #endif
-    return setlocale(category, utf8_locale);
-#else /* __ANDROID__ */
-    return setlocale(category, "");
-#endif /* __ANDROID__ */
+    res = setlocale(category, utf8_locale);
+#else /* !defined(__ANDROID__) */
+    res = setlocale(category, "");
+#endif
+    _Py_ResetForceASCII();
+    return res;
 }
 
 
@@ -1414,6 +1417,9 @@ new_interpreter(PyThreadState **tstate_p)
         PyDict_SetItemString(interp->sysdict, "modules", modules);
         _PySys_EndInit(interp->sysdict, &interp->config);
     }
+    else if (PyErr_Occurred()) {
+        goto handle_error;
+    }
 
     bimod = _PyImport_FindBuiltin("builtins", modules);
     if (bimod != NULL) {
@@ -1422,6 +1428,9 @@ new_interpreter(PyThreadState **tstate_p)
             goto handle_error;
         Py_INCREF(interp->builtins);
     }
+    else if (PyErr_Occurred()) {
+        goto handle_error;
+    }
 
     /* initialize builtin exceptions */
     _PyExc_Init(bimod);
@@ -1615,6 +1624,10 @@ initfsencoding(PyInterpreterState *interp)
         Py_FileSystemDefaultEncoding = "utf-8";
         Py_HasFileSystemDefaultEncoding = 1;
     }
+    else if (_Py_GetForceASCII()) {
+        Py_FileSystemDefaultEncoding = "ascii";
+        Py_HasFileSystemDefaultEncoding = 1;
+    }
     else if (Py_FileSystemDefaultEncoding == NULL) {
         Py_FileSystemDefaultEncoding = get_locale_encoding();
         if (Py_FileSystemDefaultEncoding == NULL) {
@@ -2011,12 +2024,6 @@ _Py_FatalError_PrintExc(int fd)
     PyObject *exception, *v, *tb;
     int has_tb;
 
-    if (PyThreadState_GET() == NULL) {
-        /* The GIL is released: trying to acquire it is likely to deadlock,
-           just give up. */
-        return 0;
-    }
-
     PyErr_Fetch(&exception, &v, &tb);
     if (exception == NULL) {
         /* No current exception */
@@ -2121,9 +2128,30 @@ fatal_error(const char *prefix, const char *msg, int status)
     fputs("\n", stderr);
     fflush(stderr); /* it helps in Windows debug build */
 
-    /* Print the exception (if an exception is set) with its traceback,
-     * or display the current Python stack. */
-    if (!_Py_FatalError_PrintExc(fd)) {
+    /* Check if the current thread has a Python thread state
+       and holds the GIL */
+    PyThreadState *tss_tstate = PyGILState_GetThisThreadState();
+    if (tss_tstate != NULL) {
+        PyThreadState *tstate = PyThreadState_GET();
+        if (tss_tstate != tstate) {
+            /* The Python thread does not hold the GIL */
+            tss_tstate = NULL;
+        }
+    }
+    else {
+        /* Py_FatalError() has been called from a C thread
+           which has no Python thread state. */
+    }
+    int has_tstate_and_gil = (tss_tstate != NULL);
+
+    if (has_tstate_and_gil) {
+        /* If an exception is set, print the exception with its traceback */
+        if (!_Py_FatalError_PrintExc(fd)) {
+            /* No exception is set, or an exception is set without traceback */
+            _Py_FatalError_DumpTracebacks(fd);
+        }
+    }
+    else {
         _Py_FatalError_DumpTracebacks(fd);
     }
 
@@ -2134,7 +2162,7 @@ fatal_error(const char *prefix, const char *msg, int status)
     _PyFaulthandler_Fini();
 
     /* Check if the current Python thread hold the GIL */
-    if (PyThreadState_GET() != NULL) {
+    if (has_tstate_and_gil) {
         /* Flush sys.stdout and sys.stderr */
         flush_std_files();
     }
index 6799d20..24b8042 100644 (file)
@@ -17,7 +17,9 @@ double _Py_force_double(double x)
 
 /* inline assembly for getting and setting the 387 FPU control word on
    gcc/x86 */
-
+#ifdef _Py_MEMORY_SANITIZER
+__attribute__((no_sanitize_memory))
+#endif
 unsigned short _Py_get_387controlword(void) {
     unsigned short cw;
     __asm__ __volatile__ ("fnstcw %0" : "=m" (cw));
index 15761e7..8077a3e 100644 (file)
@@ -532,6 +532,7 @@ PyState_RemoveModule(struct PyModuleDef* def)
         Py_FatalError("PyState_RemoveModule: Module index out of bounds.");
         return -1;
     }
+    Py_INCREF(Py_None);
     return PyList_SetItem(state->modules_by_index, index, Py_None);
 }
 
index 461e8dc..fea7e45 100644 (file)
@@ -398,6 +398,9 @@ _Py_string_to_number_with_underscores(
     }
 
     dup = PyMem_Malloc(orig_len + 1);
+    if (dup == NULL) {
+        return PyErr_NoMemory();
+    }
     end = dup;
     prev = '\0';
     last = s + orig_len;
index 81eea95..177bb6d 100644 (file)
@@ -215,8 +215,10 @@ symtable_new(void)
     struct symtable *st;
 
     st = (struct symtable *)PyMem_Malloc(sizeof(struct symtable));
-    if (st == NULL)
+    if (st == NULL) {
+        PyErr_NoMemory();
         return NULL;
+    }
 
     st->st_filename = NULL;
     st->st_blocks = NULL;
index 498fa91..efe5b29 100644 (file)
@@ -1757,7 +1757,6 @@ get_warnoptions(void)
          * call optional for embedding applications, thus making this
          * reachable again.
          */
-        Py_XDECREF(warnoptions);
         warnoptions = PyList_New(0);
         if (warnoptions == NULL)
             return NULL;
@@ -1824,7 +1823,8 @@ int
 PySys_HasWarnOptions(void)
 {
     PyObject *warnoptions = _PySys_GetObjectId(&PyId_warnoptions);
-    return (warnoptions != NULL && (PyList_Size(warnoptions) > 0)) ? 1 : 0;
+    return (warnoptions != NULL && PyList_Check(warnoptions)
+            && PyList_GET_SIZE(warnoptions) > 0);
 }
 
 static PyObject *
@@ -1842,7 +1842,6 @@ get_xoptions(void)
          * call optional for embedding applications, thus making this
          * reachable again.
          */
-        Py_XDECREF(xoptions);
         xoptions = PyDict_New();
         if (xoptions == NULL)
             return NULL;
@@ -2544,7 +2543,7 @@ makepathobject(const wchar_t *path, wchar_t delim)
             Py_DECREF(v);
             return NULL;
         }
-        PyList_SetItem(v, i, w);
+        PyList_SET_ITEM(v, i, w);
         if (*p == '\0')
             break;
         path = p+1;
index 6971405..f79f9b9 100644 (file)
@@ -152,6 +152,28 @@ PyThread__init_thread(void)
  * Thread support.
  */
 
+/* bpo-33015: pythread_callback struct and pythread_wrapper() cast
+   "void func(void *)" to "void* func(void *)": always return NULL.
+
+   PyThread_start_new_thread() uses "void func(void *)" type, whereas
+   pthread_create() requires a void* return value. */
+typedef struct {
+    void (*func) (void *);
+    void *arg;
+} pythread_callback;
+
+static void *
+pythread_wrapper(void *arg)
+{
+    /* copy func and func_arg and free the temporary structure */
+    pythread_callback *callback = arg;
+    void (*func)(void *) = callback->func;
+    void *func_arg = callback->arg;
+    PyMem_RawFree(arg);
+
+    func(func_arg);
+    return NULL;
+}
 
 unsigned long
 PyThread_start_new_thread(void (*func)(void *), void *arg)
@@ -188,21 +210,31 @@ PyThread_start_new_thread(void (*func)(void *), void *arg)
     pthread_attr_setscope(&attrs, PTHREAD_SCOPE_SYSTEM);
 #endif
 
+    pythread_callback *callback = PyMem_RawMalloc(sizeof(pythread_callback));
+
+    if (callback == NULL) {
+      return PYTHREAD_INVALID_THREAD_ID;
+    }
+
+    callback->func = func;
+    callback->arg = arg;
+
     status = pthread_create(&th,
 #if defined(THREAD_STACK_SIZE) || defined(PTHREAD_SYSTEM_SCHED_SUPPORTED)
                              &attrs,
 #else
                              (pthread_attr_t*)NULL,
 #endif
-                             (void* (*)(void *))func,
-                             (void *)arg
-                             );
+                             pythread_wrapper, callback);
 
 #if defined(THREAD_STACK_SIZE) || defined(PTHREAD_SYSTEM_SCHED_SUPPORTED)
     pthread_attr_destroy(&attrs);
 #endif
-    if (status != 0)
+
+    if (status != 0) {
+        PyMem_RawFree(callback);
         return PYTHREAD_INVALID_THREAD_ID;
+    }
 
     pthread_detach(th);
 
index 95bef64..8b32864 100644 (file)
@@ -178,11 +178,12 @@ tb_traverse(PyTracebackObject *tb, visitproc visit, void *arg)
     return 0;
 }
 
-static void
+static int
 tb_clear(PyTracebackObject *tb)
 {
     Py_CLEAR(tb->tb_next);
     Py_CLEAR(tb->tb_frame);
+    return 0;
 }
 
 PyTypeObject PyTraceBack_Type = {
index 9b08049..43ded3e 100644 (file)
@@ -1,4 +1,4 @@
-This is Python version 3.7.1
+This is Python version 3.7.2
 ============================
 
 .. image:: https://travis-ci.org/python/cpython.svg?branch=master
index 27eabcb..bfaa940 100755 (executable)
@@ -934,35 +934,50 @@ class PyFrameObjectPtr(PyObjectPtr):
         if long(f_trace) != 0:
             # we have a non-NULL f_trace:
             return self.f_lineno
-        else:
-            #try:
+
+        try:
             return self.co.addr2line(self.f_lasti)
-            #except ValueError:
-            #    return self.f_lineno
+        except Exception:
+            # bpo-34989: addr2line() is a complex function, it can fail in many
+            # ways. For example, it fails with a TypeError on "FakeRepr" if
+            # gdb fails to load debug symbols. Use a catch-all "except
+            # Exception" to make the whole function safe. The caller has to
+            # handle None anyway for optimized Python.
+            return None
 
     def current_line(self):
         '''Get the text of the current source line as a string, with a trailing
         newline character'''
         if self.is_optimized_out():
             return '(frame information optimized out)'
+
+        lineno = self.current_line_num()
+        if lineno is None:
+            return '(failed to get frame line number)'
+
         filename = self.filename()
         try:
-            f = open(os_fsencode(filename), 'r')
+            with open(os_fsencode(filename), 'r') as fp:
+                lines = fp.readlines()
         except IOError:
             return None
-        with f:
-            all_lines = f.readlines()
-            # Convert from 1-based current_line_num to 0-based list offset:
-            return all_lines[self.current_line_num()-1]
+
+        try:
+            # Convert from 1-based current_line_num to 0-based list offset
+            return lines[lineno - 1]
+        except IndexError:
+            return None
 
     def write_repr(self, out, visited):
         if self.is_optimized_out():
             out.write('(frame information optimized out)')
             return
-        out.write('Frame 0x%x, for file %s, line %i, in %s ('
+        lineno = self.current_line_num()
+        lineno = str(lineno) if lineno is not None else "?"
+        out.write('Frame 0x%x, for file %s, line %s, in %s ('
                   % (self.as_address(),
                      self.co_filename.proxyval(visited),
-                     self.current_line_num(),
+                     lineno,
                      self.co_name.proxyval(visited)))
         first = True
         for pyop_name, pyop_value in self.iter_locals():
@@ -981,9 +996,11 @@ class PyFrameObjectPtr(PyObjectPtr):
             sys.stdout.write('  (frame information optimized out)\n')
             return
         visited = set()
-        sys.stdout.write('  File "%s", line %i, in %s\n'
+        lineno = self.current_line_num()
+        lineno = str(lineno) if lineno is not None else "?"
+        sys.stdout.write('  File "%s", line %s, in %s\n'
                   % (self.co_filename.proxyval(visited),
-                     self.current_line_num(),
+                     lineno,
                      self.co_name.proxyval(visited)))
 
 class PySetObjectPtr(PyObjectPtr):
@@ -1732,6 +1749,9 @@ class PyList(gdb.Command):
 
         filename = pyop.filename()
         lineno = pyop.current_line_num()
+        if lineno is None:
+            print('Unable to read python frame line number')
+            return
 
         if start is None:
             start = lineno - 5
index 359622b..c9d0d75 100644 (file)
@@ -37,6 +37,7 @@ set BUILDX64=
 set TARGET=Rebuild\r
 set TESTTARGETDIR=\r
 set PGO=-m test -q --pgo\r
+set BUILDMSI=1\r
 set BUILDNUGET=1\r
 set BUILDZIP=1\r
 \r
@@ -61,6 +62,7 @@ if "%1" EQU "--pgo" (set PGO=%~2) && shift && shift && goto CheckOpts
 if "%1" EQU "--skip-pgo" (set PGO=) && shift && goto CheckOpts\r
 if "%1" EQU "--skip-nuget" (set BUILDNUGET=) && shift && goto CheckOpts\r
 if "%1" EQU "--skip-zip" (set BUILDZIP=) && shift && goto CheckOpts\r
+if "%1" EQU "--skip-msi" (set BUILDMSI=) && shift && goto CheckOpts\r
 \r
 if "%1" NEQ "" echo Invalid option: "%1" && exit /B 1\r
 \r
@@ -174,10 +176,12 @@ if "%OUTDIR_PLAT%" EQU "win32" (
 )\r
 \r
 set BUILDOPTS=/p:Platform=%1 /p:BuildForRelease=true /p:DownloadUrl=%DOWNLOAD_URL% /p:DownloadUrlBase=%DOWNLOAD_URL_BASE% /p:ReleaseUri=%RELEASE_URI%\r
-%MSBUILD% "%D%bundle\releaselocal.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=true\r
-if errorlevel 1 exit /B\r
-%MSBUILD% "%D%bundle\releaseweb.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=false\r
-if errorlevel 1 exit /B\r
+if defined BUILDMSI (\r
+    %MSBUILD% "%D%bundle\releaselocal.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=true\r
+    if errorlevel 1 exit /B\r
+    %MSBUILD% "%D%bundle\releaseweb.wixproj" /t:Rebuild %BUILDOPTS% %CERTOPTS% /p:RebuildAll=false\r
+    if errorlevel 1 exit /B\r
+)\r
 \r
 if defined BUILDZIP (\r
     %MSBUILD% "%D%make_zip.proj" /t:Build %BUILDOPTS% %CERTOPTS% /p:OutputPath="%BUILD%en-us"\r
@@ -214,6 +218,7 @@ echo    --skip-build (-B)   Do not build Python (just do the installers)
 echo    --skip-doc (-D)     Do not build documentation\r
 echo    --pgo               Specify PGO command for x64 installers\r
 echo    --skip-pgo          Build x64 installers without using PGO\r
+echo    --skip-msi          Do not build executable/MSI packages\r
 echo    --skip-nuget        Do not build Nuget packages\r
 echo    --skip-zip          Do not build embeddable package\r
 echo    --download          Specify the full download URL for MSIs\r
index 682b660..bc3a19c 100644 (file)
@@ -21,7 +21,7 @@
         <EmbeddedResource Include="*.wxl" />
     </ItemGroup>
     <ItemGroup>
-        <InstallFiles Include="$(PySourcePath)include\*.h">
+        <InstallFiles Include="$(PySourcePath)include\**\*.h">
             <SourceBase>$(PySourcePath)</SourceBase>
             <Source>!(bindpath.src)</Source>
             <TargetBase>$(PySourcePath)</TargetBase>
@@ -29,7 +29,7 @@
             <Group>dev_include</Group>
         </InstallFiles>
     </ItemGroup>
-    
+
     <Target Name="BuildMinGWLib"
             Inputs="$(BuildPath)$(PyDllName).dll"
             Outputs="$(BuildPath)lib$(PyDllName).a"
@@ -46,4 +46,4 @@
     </Target>
 
     <Import Project="..\msi.targets" />
-</Project>
\ No newline at end of file
+</Project>
index 46ddcb4..4bd0c57 100644 (file)
@@ -1,7 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
-    <?define exts=pyexpat;select;unicodedata;winsound;_bz2;_elementtree;_socket;_ssl;_msi;_ctypes;_hashlib;_multiprocessing;_lzma;_decimal;_overlapped;_sqlite3;_asyncio;_queue;_distutils_findvs;_contextvars ?>
+    <?define exts=pyexpat;select;unicodedata;winsound;_bz2;_elementtree;_socket;_ssl;_msi;_ctypes;_hashlib;_multiprocessing;_lzma;_decimal;_overlapped;_sqlite3;_asyncio;_queue;_contextvars ?>
     <Fragment>
+        <DirectoryRef Id="Lib_venv_scripts_nt" />
+
         <ComponentGroup Id="lib_extensions">
             <?foreach ext in $(var.exts)?>
         
             <Component Id="libssl.dll" Directory="DLLs" Guid="*">
                 <File Name="libssl$(var.ssltag).dll" KeyPath="yes" />
             </Component>
+            <Component Id="venvlauncher.exe" Directory="Lib_venv_scripts_nt" Guid="*">
+                <File Name="python.exe" Source="venvlauncher.exe" KeyPath="yes" />
+            </Component>
+            <Component Id="venvwlauncher.exe" Directory="Lib_venv_scripts_nt" Guid="*">
+                <File Name="pythonw.exe" Source="venvwlauncher.exe" KeyPath="yes" />
+            </Component>
         </ComponentGroup>
     </Fragment>
     
     <Fragment>
+        <!-- The auto-generated directory is not available when building symbols -->
+        <DirectoryRef Id="Lib">
+            <Directory Id="Lib_venv__pdbs" Name="venv">
+                <Directory Id="Lib_venv_scripts__pdbs" Name="scripts">
+                    <Directory Id="Lib_venv_scripts_nt__pdbs" Name="nt" />
+                </Directory>
+            </Directory>
+        </DirectoryRef>
+
         <ComponentGroup Id="lib_extensions_symbols">
             <?foreach ext in $(var.exts)?>
             
             <Component Id="libssl.pdb" Directory="DLLs" Guid="*">
                 <File Name="libssl$(var.ssltag).pdb" KeyPath="yes" />
             </Component>
+            <Component Id="venvlauncher.pdb" Directory="Lib_venv_scripts_nt__pdbs" Guid="*">
+                <File Name="python.pdb" Source="venvlauncher.pdb" KeyPath="yes" />
+            </Component>
+            <Component Id="venvwlauncher.pdb" Directory="Lib_venv_scripts_nt__pdbs" Guid="*">
+                <File Name="pythonw.pdb" Source="venvwlauncher.pdb" KeyPath="yes" />
+            </Component>
         </ComponentGroup>
     </Fragment>
     
diff --git a/Tools/msi/make_appx.ps1 b/Tools/msi/make_appx.ps1
new file mode 100644 (file)
index 0000000..9ca2b35
--- /dev/null
@@ -0,0 +1,71 @@
+<#\r
+.Synopsis\r
+    Compiles and signs an APPX package\r
+.Description\r
+    Given the file listing, ensures all the contents are signed\r
+    and builds and signs the final package.\r
+.Parameter mapfile\r
+    The location on disk of the text mapping file.\r
+.Parameter msix\r
+    The path and name to store the APPX/MSIX.\r
+.Parameter sign\r
+    When set, signs the APPX/MSIX. Packages to be published to\r
+    the store should not be signed.\r
+.Parameter description\r
+    Description to embed in the signature (optional).\r
+.Parameter certname\r
+    The name of the certificate to sign with (optional).\r
+.Parameter certsha1\r
+    The SHA1 hash of the certificate to sign with (optional).\r
+#>\r
+param(\r
+    [Parameter(Mandatory=$true)][string]$layout,\r
+    [Parameter(Mandatory=$true)][string]$msix,\r
+    [switch]$sign,\r
+    [string]$description,\r
+    [string]$certname,\r
+    [string]$certsha1,\r
+    [string]$certfile\r
+)\r
+\r
+$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent;\r
+Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force\r
+\r
+Set-Alias makeappx (Find-Tool "makeappx.exe") -Scope Script\r
+Set-Alias makepri (Find-Tool "makepri.exe") -Scope Script\r
+\r
+$msixdir = Split-Path $msix -Parent\r
+if ($msixdir) {\r
+    $msixdir = (mkdir -Force $msixdir).FullName\r
+} else {\r
+    $msixdir = Get-Location\r
+}\r
+$msix = Join-Path $msixdir (Split-Path $msix -Leaf)\r
+\r
+pushd $layout\r
+try {\r
+    if (Test-Path resources.pri) {\r
+        del resources.pri\r
+    }\r
+    $name = ([xml](gc AppxManifest.xml)).Package.Identity.Name\r
+    makepri new /pr . /mn AppxManifest.xml /in $name /cf _resources.xml /of _resources.pri /mf appx /o\r
+    if (-not $? -or -not (Test-Path _resources.map.txt)) {\r
+        throw "makepri step failed"\r
+    }\r
+    $lines = gc _resources.map.txt\r
+    $lines | ?{ -not ($_ -match '"_resources[\w\.]+?"') } | Out-File _resources.map.txt -Encoding utf8\r
+    makeappx pack /f _resources.map.txt /m AppxManifest.xml /o /p $msix\r
+    if (-not $?) {\r
+        throw "makeappx step failed"\r
+    }\r
+} finally {\r
+    popd\r
+}\r
+\r
+if ($sign) {\r
+    Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files $msix\r
+\r
+    if (-not $?) {\r
+        throw "Package signing failed"\r
+    }\r
+}\r
diff --git a/Tools/msi/make_cat.ps1 b/Tools/msi/make_cat.ps1
new file mode 100644 (file)
index 0000000..a04fb61
--- /dev/null
@@ -0,0 +1,34 @@
+<#\r
+.Synopsis\r
+    Compiles and signs a catalog file.\r
+.Description\r
+    Given the CDF definition file, builds and signs a catalog.\r
+.Parameter catalog\r
+    The path to the catalog definition file to compile and\r
+    sign. It is assumed that the .cat file will be the same\r
+    name with a new extension.\r
+.Parameter description\r
+    The description to add to the signature (optional).\r
+.Parameter certname\r
+    The name of the certificate to sign with (optional).\r
+.Parameter certsha1\r
+    The SHA1 hash of the certificate to sign with (optional).\r
+#>\r
+param(\r
+    [Parameter(Mandatory=$true)][string]$catalog,\r
+    [string]$description,\r
+    [string]$certname,\r
+    [string]$certsha1,\r
+    [string]$certfile\r
+)\r
+\r
+$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent;\r
+Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force\r
+\r
+Set-Alias MakeCat (Find-Tool "makecat.exe") -Scope Script\r
+\r
+MakeCat $catalog\r
+if (-not $?) {\r
+    throw "Catalog compilation failed"\r
+}\r
+Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files @($catalog -replace 'cdf$', 'cat')\r
index e83d15c..bc2f81f 100644 (file)
         <TargetExt>.zip</TargetExt>\r
         <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)" -b "$(BuildPath.TrimEnd(`\`))"</Arguments>\r
-        <Environment>set DOC_FILENAME=python$(PythonVersion).chm</Environment>\r
+        <Arguments>"$(PythonExe)" "$(PySourcePath)PC\layout"</Arguments>\r
+        <Arguments>$(Arguments) -b "$(BuildPath.TrimEnd(`\`))" -s "$(PySourcePath.TrimEnd(`\`))"</Arguments>\r
+        <Arguments>$(Arguments) -t "$(IntermediateOutputPath)\zip_$(ArchName)"</Arguments>\r
+        <Arguments>$(Arguments) --zip "$(TargetPath)"</Arguments>\r
+        <Arguments>$(Arguments) --precompile --zip-lib --include-underpth --include-stable --flat-dlls</Arguments>\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
     <Target Name="_Build">\r
diff --git a/Tools/msi/make_zip.py b/Tools/msi/make_zip.py
deleted file mode 100644 (file)
index 58f3b15..0000000
+++ /dev/null
@@ -1,250 +0,0 @@
-import argparse
-import py_compile
-import re
-import sys
-import shutil
-import stat
-import os
-import tempfile
-
-from itertools import chain
-from pathlib import Path
-from zipfile import ZipFile, ZIP_DEFLATED
-
-
-TKTCL_RE = re.compile(r'^(_?tk|tcl).+\.(pyd|dll)', re.IGNORECASE)
-DEBUG_RE = re.compile(r'_d\.(pyd|dll|exe|pdb|lib)$', re.IGNORECASE)
-PYTHON_DLL_RE = re.compile(r'python\d\d?\.dll$', re.IGNORECASE)
-
-DEBUG_FILES = {
-    '_ctypes_test',
-    '_testbuffer',
-    '_testcapi',
-    '_testconsole',
-    '_testimportmultiple',
-    '_testmultiphase',
-    'xxlimited',
-    'python3_dstub',
-}
-
-EXCLUDE_FROM_LIBRARY = {
-    '__pycache__',
-    'idlelib',
-    'pydoc_data',
-    'site-packages',
-    'tkinter',
-    'turtledemo',
-}
-
-EXCLUDE_FROM_EMBEDDABLE_LIBRARY = {
-    'ensurepip',
-    'venv',
-}
-
-EXCLUDE_FILE_FROM_LIBRARY = {
-    'bdist_wininst.py',
-}
-
-EXCLUDE_FILE_FROM_LIBS = {
-    'liblzma',
-    'python3stub',
-}
-
-EXCLUDED_FILES = {
-    'pyshellext',
-}
-
-def is_not_debug(p):
-    if DEBUG_RE.search(p.name):
-        return False
-
-    if TKTCL_RE.search(p.name):
-        return False
-
-    return p.stem.lower() not in DEBUG_FILES and p.stem.lower() not in EXCLUDED_FILES
-
-def is_not_debug_or_python(p):
-    return is_not_debug(p) and not PYTHON_DLL_RE.search(p.name)
-
-def include_in_lib(p):
-    name = p.name.lower()
-    if p.is_dir():
-        if name in EXCLUDE_FROM_LIBRARY:
-            return False
-        if name == 'test' and p.parts[-2].lower() == 'lib':
-            return False
-        if name in {'test', 'tests'} and p.parts[-3].lower() == 'lib':
-            return False
-        return True
-
-    if name in EXCLUDE_FILE_FROM_LIBRARY:
-        return False
-
-    suffix = p.suffix.lower()
-    return suffix not in {'.pyc', '.pyo', '.exe'}
-
-def include_in_embeddable_lib(p):
-    if p.is_dir() and p.name.lower() in EXCLUDE_FROM_EMBEDDABLE_LIBRARY:
-        return False
-
-    return include_in_lib(p)
-
-def include_in_libs(p):
-    if not is_not_debug(p):
-        return False
-
-    return p.stem.lower() not in EXCLUDE_FILE_FROM_LIBS
-
-def include_in_tools(p):
-    if p.is_dir() and p.name.lower() in {'scripts', 'i18n', 'pynche', 'demo', 'parser'}:
-        return True
-
-    return p.suffix.lower() in {'.py', '.pyw', '.txt'}
-
-BASE_NAME = 'python{0.major}{0.minor}'.format(sys.version_info)
-
-FULL_LAYOUT = [
-    ('/', '$build', 'python.exe', is_not_debug),
-    ('/', '$build', 'pythonw.exe', is_not_debug),
-    ('/', '$build', 'python{}.dll'.format(sys.version_info.major), is_not_debug),
-    ('/', '$build', '{}.dll'.format(BASE_NAME), is_not_debug),
-    ('DLLs/', '$build', '*.pyd', is_not_debug),
-    ('DLLs/', '$build', '*.dll', is_not_debug_or_python),
-    ('include/', 'include', '*.h', None),
-    ('include/', 'PC', 'pyconfig.h', None),
-    ('Lib/', 'Lib', '**/*', include_in_lib),
-    ('libs/', '$build', '*.lib', include_in_libs),
-    ('Tools/', 'Tools', '**/*', include_in_tools),
-]
-
-EMBED_LAYOUT = [
-    ('/', '$build', 'python*.exe', is_not_debug),
-    ('/', '$build', '*.pyd', is_not_debug),
-    ('/', '$build', '*.dll', is_not_debug),
-    ('{}.zip'.format(BASE_NAME), 'Lib', '**/*', include_in_embeddable_lib),
-]
-
-if os.getenv('DOC_FILENAME'):
-    FULL_LAYOUT.append(('Doc/', 'Doc/build/htmlhelp', os.getenv('DOC_FILENAME'), None))
-if os.getenv('VCREDIST_PATH'):
-    FULL_LAYOUT.append(('/', os.getenv('VCREDIST_PATH'), 'vcruntime*.dll', None))
-    EMBED_LAYOUT.append(('/', os.getenv('VCREDIST_PATH'), 'vcruntime*.dll', None))
-
-def copy_to_layout(target, rel_sources):
-    count = 0
-
-    if target.suffix.lower() == '.zip':
-        if target.exists():
-            target.unlink()
-
-        with ZipFile(str(target), 'w', ZIP_DEFLATED) as f:
-            with tempfile.TemporaryDirectory() as tmpdir:
-                for s, rel in rel_sources:
-                    if rel.suffix.lower() == '.py':
-                        pyc = Path(tmpdir) / rel.with_suffix('.pyc').name
-                        try:
-                            py_compile.compile(str(s), str(pyc), str(rel), doraise=True, optimize=2)
-                        except py_compile.PyCompileError:
-                            f.write(str(s), str(rel))
-                        else:
-                            f.write(str(pyc), str(rel.with_suffix('.pyc')))
-                    else:
-                        f.write(str(s), str(rel))
-                    count += 1
-
-    else:
-        for s, rel in rel_sources:
-            dest = target / rel
-            try:
-                dest.parent.mkdir(parents=True)
-            except FileExistsError:
-                pass
-            if dest.is_file():
-                dest.chmod(stat.S_IWRITE)
-            shutil.copy(str(s), str(dest))
-            if dest.is_file():
-                dest.chmod(stat.S_IWRITE)
-            count += 1
-
-    return count
-
-def rglob(root, pattern, condition):
-    dirs = [root]
-    recurse = pattern[:3] in {'**/', '**\\'}
-    while dirs:
-        d = dirs.pop(0)
-        for f in d.glob(pattern[3:] if recurse else pattern):
-            if recurse and f.is_dir() and (not condition or condition(f)):
-                dirs.append(f)
-            elif f.is_file() and (not condition or condition(f)):
-                yield f, f.relative_to(root)
-
-def main():
-    parser = argparse.ArgumentParser()
-    parser.add_argument('-s', '--source', metavar='dir', help='The directory containing the repository root', type=Path)
-    parser.add_argument('-o', '--out', metavar='file', help='The name of the output archive', type=Path, default=None)
-    parser.add_argument('-t', '--temp', metavar='dir', help='A directory to temporarily extract files into', type=Path, default=None)
-    parser.add_argument('-e', '--embed', help='Create an embedding layout', action='store_true', default=False)
-    parser.add_argument('-b', '--build', help='Specify the build directory', type=Path, default=None)
-    ns = parser.parse_args()
-
-    source = ns.source or (Path(__file__).resolve().parent.parent.parent)
-    out = ns.out
-    build = ns.build or Path(sys.exec_prefix)
-    assert isinstance(source, Path)
-    assert not out or isinstance(out, Path)
-    assert isinstance(build, Path)
-
-    if ns.temp:
-        temp = ns.temp
-        delete_temp = False
-    else:
-        temp = Path(tempfile.mkdtemp())
-        delete_temp = True
-
-    if out:
-        try:
-            out.parent.mkdir(parents=True)
-        except FileExistsError:
-            pass
-    try:
-        temp.mkdir(parents=True)
-    except FileExistsError:
-        pass
-
-    layout = EMBED_LAYOUT if ns.embed else FULL_LAYOUT
-
-    try:
-        for t, s, p, c in layout:
-            if s == '$build':
-                fs = build
-            else:
-                fs = source / s
-            files = rglob(fs, p, c)
-            extra_files = []
-            if s == 'Lib' and p == '**/*':
-                extra_files.append((
-                    source / 'tools' / 'msi' / 'distutils.command.bdist_wininst.py',
-                    Path('distutils') / 'command' / 'bdist_wininst.py'
-                ))
-            copied = copy_to_layout(temp / t.rstrip('/'), chain(files, extra_files))
-            print('Copied {} files'.format(copied))
-
-        if ns.embed:
-            with open(str(temp / (BASE_NAME + '._pth')), 'w') as f:
-                print(BASE_NAME + '.zip', file=f)
-                print('.', file=f)
-                print('', file=f)
-                print('# Uncomment to run site.main() automatically', file=f)
-                print('#import site', file=f)
-
-        if out:
-            total = copy_to_layout(out, rglob(temp, '**/*', None))
-            print('Wrote {} files to {}'.format(total, out))
-    finally:
-        if delete_temp:
-            shutil.rmtree(temp, True)
-
-
-if __name__ == "__main__":
-    sys.exit(int(main() or 0))
diff --git a/Tools/msi/sdktools.psm1 b/Tools/msi/sdktools.psm1
new file mode 100644 (file)
index 0000000..81a74d3
--- /dev/null
@@ -0,0 +1,43 @@
+function Find-Tool {
+    param([string]$toolname)
+
+    $kitroot = (gp 'HKLM:\SOFTWARE\Microsoft\Windows Kits\Installed Roots\').KitsRoot10
+    $tool = (gci -r "$kitroot\Bin\*\x64\$toolname" | sort FullName -Desc | select -First 1)
+    if (-not $tool) {
+        throw "$toolname is not available"
+    }
+    Write-Host "Found $toolname at $($tool.FullName)"
+    return $tool.FullName
+}
+
+Set-Alias SignTool (Find-Tool "signtool.exe") -Scope Script
+
+function Sign-File {
+    param([string]$certname, [string]$certsha1, [string]$certfile, [string]$description, [string[]]$files)
+
+    if (-not $description) {
+        $description = $env:SigningDescription;
+        if (-not $description) {
+            $description = "Python";
+        }
+    }
+    if (-not $certname) {
+        $certname = $env:SigningCertificate;
+    }
+    if (-not $certfile) {
+        $certfile = $env:SigningCertificateFile;
+    }
+
+    foreach ($a in $files) {
+        if ($certsha1) {
+            SignTool sign /sha1 $certsha1 /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a
+        } elseif ($certname) {
+            SignTool sign /n $certname /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a
+        } elseif ($certfile) {
+            SignTool sign /f $certfile /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a
+        } else {
+            SignTool sign /a /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a
+        }
+    }
+}
+
diff --git a/Tools/msi/sign_build.ps1 b/Tools/msi/sign_build.ps1
new file mode 100644 (file)
index 0000000..55d0de4
--- /dev/null
@@ -0,0 +1,34 @@
+<#\r
+.Synopsis\r
+    Recursively signs the contents of a directory.\r
+.Description\r
+    Given the file patterns, code signs the contents.\r
+.Parameter root\r
+    The root directory to sign.\r
+.Parameter patterns\r
+    The file patterns to sign\r
+.Parameter description\r
+    The description to add to the signature (optional).\r
+.Parameter certname\r
+    The name of the certificate to sign with (optional).\r
+.Parameter certsha1\r
+    The SHA1 hash of the certificate to sign with (optional).\r
+#>\r
+param(\r
+    [Parameter(Mandatory=$true)][string]$root,\r
+    [string[]]$patterns=@("*.exe", "*.dll", "*.pyd"),\r
+    [string]$description,\r
+    [string]$certname,\r
+    [string]$certsha1,\r
+    [string]$certfile\r
+)\r
+\r
+$tools = $script:MyInvocation.MyCommand.Path | Split-Path -parent;\r
+Import-Module $tools\sdktools.psm1 -WarningAction SilentlyContinue -Force\r
+\r
+pushd $root\r
+try {\r
+    Sign-File -certname $certname -certsha1 $certsha1 -certfile $certfile -description $description -files (gci -r $patterns)\r
+} finally {\r
+    popd\r
+}
\ No newline at end of file
index ba6c365..4457f3f 100644 (file)
         <SignOutput>false</SignOutput>\r
         <TargetName>$(OutputName).$(NuspecVersion)</TargetName>\r
         <TargetExt>.nupkg</TargetExt>\r
-        <IntermediateOutputPath>$(IntermediateOutputPath)\nuget_$(ArchName)</IntermediateOutputPath>\r
+        <IntermediateOutputPath>$(IntermediateOutputPath)\nuget_$(ArchName)\</IntermediateOutputPath>\r
         \r
-        <CleanCommand>rmdir /q/s "$(IntermediateOutputPath)"</CleanCommand>\r
+        <CleanCommand>rmdir /q/s "$(IntermediateOutputPath.TrimEnd(`\`))"</CleanCommand>\r
         \r
-        <PythonArguments>"$(PythonExe)" "$(MSBuildThisFileDirectory)\..\msi\make_zip.py"</PythonArguments>\r
-        <PythonArguments>$(PythonArguments) -t "$(IntermediateOutputPath)" -b "$(BuildPath.TrimEnd(`\`))"</PythonArguments>\r
+        <PythonArguments>"$(PythonExe)" "$(PySourcePath)PC\layout"</PythonArguments>\r
+        <PythonArguments>$(PythonArguments) -b "$(BuildPath.TrimEnd(`\`))" -s "$(PySourcePath.TrimEnd(`\`))"</PythonArguments>\r
+        <PythonArguments>$(PythonArguments) -t "$(IntermediateOutputPath)obj"</PythonArguments>\r
+        <PythonArguments>$(PythonArguments) --copy "$(IntermediateOutputPath)pkg"</PythonArguments>\r
+        <PythonArguments>$(PythonArguments) --include-dev --include-tools --include-pip --include-stable --include-launcher --include-props</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
+        <PackageArguments Condition="$(Packages) != ''">"$(IntermediateOutputPath)pkg\pip.exe" -B -m pip install -U $(Packages)</PackageArguments>\r
         \r
-        <NugetPackCommand>"$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).nuspec" -BasePath "$(IntermediateOutputPath)"</NugetPackCommand>\r
+        <NugetPackCommand>"$(Nuget)" pack "$(MSBuildThisFileDirectory)\$(OutputName).nuspec" -BasePath "$(IntermediateOutputPath)pkg"</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%0Aset PYTHON_NUSPEC_VERSION=$(NuspecVersion)</Environment>\r
+        <Environment Condition="$(Platform) != 'x86'">$(Environment)%0D%0Aset PYTHON_PROPS_PLATFORM=$(Platform)</Environment>\r
+        <Environment Condition="$(Platform) == 'x86'">$(Environment)%0D%0Aset PYTHON_PROPS_PLATFORM=Win32</Environment>\r
         <Environment>$(Environment)%0D%0Amkdir "$(OutputPath.Trim(`\`))" &gt;nul 2&gt;nul</Environment>\r
     </PropertyGroup>\r
 \r
 \r
     <Target Name="_Build">\r
         <Exec Command="$(CleanCommand)" />\r
-        <Exec Command="setlocal%0D%0A$(Environment)%0D%0A$(PythonArguments)" />\r
-        <Exec Command="$(PipArguments)" />\r
-        <Exec Command="$(PackageArguments)" Condition="$(PackageArguments) != ''" />\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
+        <Exec Command="setlocal%0D%0A$(Environment)%0D%0A$(PythonArguments)%0D%0A$(PackageArguments)" />\r
 \r
         <Exec Command="$(NugetPackCommand) $(NugetArguments)" />\r
         <Exec Command="$(NugetPackSymbolsCommand) $(NugetArguments)" Condition="$(NugetPackSymbolsCommand) != ''" />\r
index a1253d1..8a8480a 100755 (executable)
@@ -48,7 +48,9 @@ def get_git_branch():
     """Get the symbolic name for the current git branch"""
     cmd = "git rev-parse --abbrev-ref HEAD".split()
     try:
-        return subprocess.check_output(cmd, stderr=subprocess.DEVNULL)
+        return subprocess.check_output(cmd,
+                                       stderr=subprocess.DEVNULL,
+                                       cwd=SRCDIR)
     except subprocess.CalledProcessError:
         return None
 
@@ -60,7 +62,9 @@ def get_git_upstream_remote():
     """
     cmd = "git remote get-url upstream".split()
     try:
-        subprocess.check_output(cmd, stderr=subprocess.DEVNULL)
+        subprocess.check_output(cmd,
+                                stderr=subprocess.DEVNULL,
+                                cwd=SRCDIR)
     except subprocess.CalledProcessError:
         return "origin"
     return "upstream"
@@ -98,7 +102,9 @@ def changed_files(base_branch=None):
         else:
             cmd = 'git status --porcelain'
         filenames = []
-        with subprocess.Popen(cmd.split(), stdout=subprocess.PIPE) as st:
+        with subprocess.Popen(cmd.split(),
+                              stdout=subprocess.PIPE,
+                              cwd=SRCDIR) as st:
             for line in st.stdout:
                 line = line.decode().rstrip()
                 status_text, filename = line.split(maxsplit=1)
index 9c1e9fe..5565c21 100755 (executable)
@@ -1806,7 +1806,7 @@ class HTMLHelp:
             print('<!-- This file defines the table of contents -->', file=fp)
             print('<HTML>', file=fp)
             print('<HEAD>', file=fp)
-            print('<meta name="GENERATOR"'
+            print('<meta name="GENERATOR" '
                         'content="Microsoft&reg; HTML Help Workshop 4.1">', file=fp)
             print('<!-- Sitemap 1.0 -->', file=fp)
             print('</HEAD>', file=fp)
@@ -1831,7 +1831,7 @@ class HTMLHelp:
             print('<!-- This file defines the index -->', file=fp)
             print('<HTML>', file=fp)
             print('<HEAD>', file=fp)
-            print('<meta name="GENERATOR"'
+            print('<meta name="GENERATOR" '
                         'content="Microsoft&reg; HTML Help Workshop 4.1">', file=fp)
             print('<!-- Sitemap 1.0 -->', file=fp)
             print('</HEAD>', file=fp)
index 6a24d8e..32828c2 100644 (file)
@@ -13,7 +13,7 @@
 
 m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
 dnl pkg.m4 - Macros to locate and utilise pkg-config.   -*- Autoconf -*-
-dnl serial 11 (pkg-config-0.29.1)
+dnl serial 11 (pkg-config-0.29)
 dnl
 dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
 dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com>
@@ -55,7 +55,7 @@ dnl
 dnl See the "Since" comment for each macro you use to see what version
 dnl of the macros you require.
 m4_defun([PKG_PREREQ],
-[m4_define([PKG_MACROS_VERSION], [0.29.1])
+[m4_define([PKG_MACROS_VERSION], [0.29])
 m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1,
     [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
 ])dnl PKG_PREREQ
index 36e117a..5e5f974 100755 (executable)
--- a/configure
+++ b/configure
@@ -667,21 +667,24 @@ SHLIB_SUFFIX
 LIBTOOL_CRUFT
 OTHER_LIBTOOL_OPT
 UNIVERSAL_ARCH_FLAGS
+LDFLAGS_NODIST
 CFLAGS_NODIST
 BASECFLAGS
 CFLAGS_ALIASING
 OPT
 LLVM_PROF_FOUND
-target_os
-target_vendor
-target_cpu
-target
 LLVM_PROFDATA
 LLVM_PROF_ERR
 LLVM_PROF_FILE
 LLVM_PROF_MERGER
 PGO_PROF_USE_FLAG
 PGO_PROF_GEN_FLAG
+LLVM_AR_FOUND
+target_os
+target_vendor
+target_cpu
+target
+LLVM_AR
 DEF_MAKE_RULE
 DEF_MAKE_ALL_RULE
 ABIFLAGS
@@ -819,6 +822,8 @@ enable_optimizations
 with_lto
 with_hash_algorithm
 with_address_sanitizer
+with_memory_sanitizer
+with_undefined_behavior_sanitizer
 with_libs
 with_system_expat
 with_system_ffi
@@ -1506,7 +1511,10 @@ Optional Packages:
   --with-hash-algorithm=[fnv|siphash24]
                           select hash algorithm
   --with-address-sanitizer
-                          enable AddressSanitizer
+                          enable AddressSanitizer (asan)
+  --with-memory-sanitizer enable MemorySanitizer (msan)
+  --with-undefined-behavior-sanitizer
+                          enable UndefinedBehaviorSanitizer (ubsan)
   --with-libs='lib1 ...'  link against additional libs
   --with-system-expat     build pyexpat module using an installed expat
                           library
@@ -6432,6 +6440,26 @@ else
   DEF_MAKE_RULE="all"
 fi
 
+# Make llvm-relatec checks work on systems where llvm tools are not installed with their
+# normal names in the default $PATH (ie: Ubuntu).  They exist under the
+# non-suffixed name in their versioned llvm directory.
+
+llvm_bin_dir=''
+llvm_path="${PATH}"
+if test "${CC}" = "clang"
+then
+  clang_bin=`which clang`
+  # Some systems install clang elsewhere as a symlink to the real path
+  # which is where the related llvm tools are located.
+  if test -L "${clang_bin}"
+  then
+    clang_dir=`dirname "${clang_bin}"`
+    clang_bin=`readlink "${clang_bin}"`
+    llvm_bin_dir="${clang_dir}/"`dirname "${clang_bin}"`
+    llvm_path="${llvm_path}${PATH_SEPARATOR}${llvm_bin_dir}"
+  fi
+fi
+
 # Enable LTO flags
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for --with-lto" >&5
 $as_echo_n "checking for --with-lto... " >&6; }
@@ -6457,65 +6485,8 @@ fi
 if test "$Py_LTO" = 'true' ; then
   case $CC in
     *clang*)
-      case $ac_sys_system in
-        Darwin*)
-          # Any changes made here should be reflected in the GCC+Darwin case below
-          LTOFLAGS="-flto -Wl,-export_dynamic"
-          ;;
-        *)
-          LTOFLAGS="-flto"
-          ;;
-      esac
-      ;;
-    *gcc*)
-      case $ac_sys_system in
-        Darwin*)
-          LTOFLAGS="-flto -Wl,-export_dynamic"
-          ;;
-        *)
-          LTOFLAGS="-flto -fuse-linker-plugin -ffat-lto-objects -flto-partition=none"
-          ;;
-      esac
-      ;;
-  esac
-
-  if test "$ac_cv_prog_cc_g" = "yes"
-  then
-      # bpo-30345: Add -g to LDFLAGS when compiling with LTO
-      # to get debug symbols.
-      LTOFLAGS="$LTOFLAGS -g"
-  fi
-
-  CFLAGS="$CFLAGS $LTOFLAGS"
-  LDFLAGS="$LDFLAGS $LTOFLAGS"
-fi
-
-# Enable PGO flags.
-
-
-
-
-
-# Make this work on systems where llvm tools are not installed with their
-# normal names in the default $PATH (ie: Ubuntu).  They exist under the
-# non-suffixed name in their versioned llvm directory.
-llvm_bin_dir=''
-llvm_path="${PATH}"
-if test "${CC}" = "clang"
-then
-  clang_bin=`which clang`
-  # Some systems install clang elsewhere as a symlink to the real path
-  # which is where the related llvm tools are located.
-  if test -L "${clang_bin}"
-  then
-    clang_dir=`dirname "${clang_bin}"`
-    clang_bin=`readlink "${clang_bin}"`
-    llvm_bin_dir="${clang_dir}/"`dirname "${clang_bin}"`
-    llvm_path="${llvm_path}${PATH_SEPARATOR}${llvm_bin_dir}"
-  fi
-fi
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking target system type" >&5
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking target system type" >&5
 $as_echo_n "checking target system type... " >&6; }
 if ${ac_cv_target+:} false; then :
   $as_echo_n "(cached) " >&6
@@ -6554,6 +6525,163 @@ test -n "$target_alias" &&
   test "$program_prefix$program_suffix$program_transform_name" = \
     NONENONEs,x,x, &&
   program_prefix=${target_alias}-
+# Extract the first word of "$target_alias-llvm-ar", so it can be a program name with args.
+set dummy $target_alias-llvm-ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_LLVM_AR+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $LLVM_AR in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_LLVM_AR="$LLVM_AR" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in ${llvm_path}
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_LLVM_AR="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+LLVM_AR=$ac_cv_path_LLVM_AR
+if test -n "$LLVM_AR"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LLVM_AR" >&5
+$as_echo "$LLVM_AR" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+if test -z "$ac_cv_path_LLVM_AR"; then
+  if test "$build" = "$target"; then
+    ac_pt_LLVM_AR=$LLVM_AR
+    # Extract the first word of "llvm-ar", so it can be a program name with args.
+set dummy llvm-ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_ac_pt_LLVM_AR+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $ac_pt_LLVM_AR in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_ac_pt_LLVM_AR="$ac_pt_LLVM_AR" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in ${llvm_path}
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_ac_pt_LLVM_AR="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  test -z "$ac_cv_path_ac_pt_LLVM_AR" && ac_cv_path_ac_pt_LLVM_AR="''"
+  ;;
+esac
+fi
+ac_pt_LLVM_AR=$ac_cv_path_ac_pt_LLVM_AR
+if test -n "$ac_pt_LLVM_AR"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_LLVM_AR" >&5
+$as_echo "$ac_pt_LLVM_AR" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+    LLVM_AR=$ac_pt_LLVM_AR
+  else
+    LLVM_AR="''"
+  fi
+else
+  LLVM_AR="$ac_cv_path_LLVM_AR"
+fi
+
+
+      if test -n "${LLVM_AR}" -a -x "${LLVM_AR}"
+      then
+        LLVM_AR_FOUND="found"
+      else
+        LLVM_AR_FOUND="not-found"
+      fi
+      if test "$ac_sys_system" = "Darwin" -a "${LLVM_AR_FOUND}" = "not-found"
+      then
+        found_llvm_ar=`/usr/bin/xcrun -find llvm-ar 2>/dev/null`
+        if test -n "${found_llvm_ar}"
+        then
+          LLVM_AR='/usr/bin/xcrun llvm-ar'
+          LLVM_AR_FOUND=found
+          { $as_echo "$as_me:${as_lineno-$LINENO}: llvm-ar found via xcrun: ${LLVM_AR}" >&5
+$as_echo "$as_me: llvm-ar found via xcrun: ${LLVM_AR}" >&6;}
+        fi
+      fi
+      if test $LLVM_AR_FOUND = not-found
+      then
+        LLVM_PROFR_ERR=yes
+        as_fn_error $? "llvm-ar is required for a --with-lto build with clang but could not be found." "$LINENO" 5
+      else
+        LLVM_AR_ERR=no
+      fi
+      AR="${LLVM_AR}"
+      case $ac_sys_system in
+        Darwin*)
+          # Any changes made here should be reflected in the GCC+Darwin case below
+          LTOFLAGS="-flto -Wl,-export_dynamic"
+          ;;
+        *)
+          LTOFLAGS="-flto"
+          ;;
+      esac
+      ;;
+    *gcc*)
+      case $ac_sys_system in
+        Darwin*)
+          LTOFLAGS="-flto -Wl,-export_dynamic"
+          ;;
+        *)
+          LTOFLAGS="-flto -fuse-linker-plugin -ffat-lto-objects -flto-partition=none"
+          ;;
+      esac
+      ;;
+  esac
+
+  if test "$ac_cv_prog_cc_g" = "yes"
+  then
+      # bpo-30345: Add -g to LDFLAGS when compiling with LTO
+      # to get debug symbols.
+      LTOFLAGS="$LTOFLAGS -g"
+  fi
+
+  CFLAGS_NODIST="$CFLAGS_NODIST $LTOFLAGS"
+  LDFLAGS_NODIST="$LDFLAGS_NODIST $LTOFLAGS"
+fi
+
+# Enable PGO flags.
+
+
+
+
+
+
 # Extract the first word of "$target_alias-llvm-profdata", so it can be a program name with args.
 set dummy $target_alias-llvm-profdata; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -6796,6 +6924,7 @@ fi
 
 
 
+
 # The -arch flags for universal builds on OSX
 UNIVERSAL_ARCH_FLAGS=
 
@@ -9965,6 +10094,44 @@ if test "${with_address_sanitizer+set}" = set; then :
 $as_echo "$withval" >&6; }
 BASECFLAGS="-fsanitize=address -fno-omit-frame-pointer $BASECFLAGS"
 LDFLAGS="-fsanitize=address $LDFLAGS"
+# ASan works by controlling memory allocation, our own malloc interferes.
+with_pymalloc="no"
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for --with-memory-sanitizer" >&5
+$as_echo_n "checking for --with-memory-sanitizer... " >&6; }
+
+# Check whether --with-memory_sanitizer was given.
+if test "${with_memory_sanitizer+set}" = set; then :
+  withval=$with_memory_sanitizer;
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $withval" >&5
+$as_echo "$withval" >&6; }
+BASECFLAGS="-fsanitize=memory -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer $BASECFLAGS"
+LDFLAGS="-fsanitize=memory -fsanitize-memory-track-origins=2 $LDFLAGS"
+# MSan works by controlling memory allocation, our own malloc interferes.
+with_pymalloc="no"
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for --with-undefined-behavior-sanitizer" >&5
+$as_echo_n "checking for --with-undefined-behavior-sanitizer... " >&6; }
+
+# Check whether --with-undefined_behavior_sanitizer was given.
+if test "${with_undefined_behavior_sanitizer+set}" = set; then :
+  withval=$with_undefined_behavior_sanitizer;
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $withval" >&5
+$as_echo "$withval" >&6; }
+BASECFLAGS="-fsanitize=undefined $BASECFLAGS"
+LDFLAGS="-fsanitize=undefined $LDFLAGS"
 
 else
   { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
index 620b48a..a7de901 100644 (file)
@@ -1302,6 +1302,26 @@ else
   DEF_MAKE_RULE="all"
 fi
 
+# Make llvm-relatec checks work on systems where llvm tools are not installed with their
+# normal names in the default $PATH (ie: Ubuntu).  They exist under the
+# non-suffixed name in their versioned llvm directory.
+
+llvm_bin_dir=''
+llvm_path="${PATH}"
+if test "${CC}" = "clang"
+then
+  clang_bin=`which clang`
+  # Some systems install clang elsewhere as a symlink to the real path
+  # which is where the related llvm tools are located.
+  if test -L "${clang_bin}"
+  then
+    clang_dir=`dirname "${clang_bin}"`
+    clang_bin=`readlink "${clang_bin}"`
+    llvm_bin_dir="${clang_dir}/"`dirname "${clang_bin}"`
+    llvm_path="${llvm_path}${PATH_SEPARATOR}${llvm_bin_dir}"
+  fi
+fi
+
 # Enable LTO flags
 AC_MSG_CHECKING(for --with-lto)
 AC_ARG_WITH(lto, AS_HELP_STRING([--with-lto], [Enable Link Time Optimization in any build. Disabled by default.]),
@@ -1318,6 +1338,33 @@ fi],
 if test "$Py_LTO" = 'true' ; then
   case $CC in
     *clang*)
+      AC_SUBST(LLVM_AR)
+      AC_PATH_TARGET_TOOL(LLVM_AR, llvm-ar, '', ${llvm_path})
+      AC_SUBST(LLVM_AR_FOUND)
+      if test -n "${LLVM_AR}" -a -x "${LLVM_AR}"
+      then
+        LLVM_AR_FOUND="found"
+      else
+        LLVM_AR_FOUND="not-found"
+      fi
+      if test "$ac_sys_system" = "Darwin" -a "${LLVM_AR_FOUND}" = "not-found"
+      then
+        found_llvm_ar=`/usr/bin/xcrun -find llvm-ar 2>/dev/null`
+        if test -n "${found_llvm_ar}"
+        then
+          LLVM_AR='/usr/bin/xcrun llvm-ar'
+          LLVM_AR_FOUND=found
+          AC_MSG_NOTICE([llvm-ar found via xcrun: ${LLVM_AR}])
+        fi
+      fi
+      if test $LLVM_AR_FOUND = not-found
+      then
+        LLVM_PROFR_ERR=yes
+        AC_MSG_ERROR([llvm-ar is required for a --with-lto build with clang but could not be found.])
+      else
+        LLVM_AR_ERR=no
+      fi
+      AR="${LLVM_AR}"
       case $ac_sys_system in
         Darwin*)
           # Any changes made here should be reflected in the GCC+Darwin case below
@@ -1347,8 +1394,8 @@ if test "$Py_LTO" = 'true' ; then
       LTOFLAGS="$LTOFLAGS -g"
   fi
 
-  CFLAGS="$CFLAGS $LTOFLAGS"
-  LDFLAGS="$LDFLAGS $LTOFLAGS"
+  CFLAGS_NODIST="$CFLAGS_NODIST $LTOFLAGS"
+  LDFLAGS_NODIST="$LDFLAGS_NODIST $LTOFLAGS"
 fi
 
 # Enable PGO flags.
@@ -1357,24 +1404,6 @@ AC_SUBST(PGO_PROF_USE_FLAG)
 AC_SUBST(LLVM_PROF_MERGER)
 AC_SUBST(LLVM_PROF_FILE)
 AC_SUBST(LLVM_PROF_ERR)
-# Make this work on systems where llvm tools are not installed with their
-# normal names in the default $PATH (ie: Ubuntu).  They exist under the
-# non-suffixed name in their versioned llvm directory.
-llvm_bin_dir=''
-llvm_path="${PATH}"
-if test "${CC}" = "clang"
-then
-  clang_bin=`which clang`
-  # Some systems install clang elsewhere as a symlink to the real path
-  # which is where the related llvm tools are located.
-  if test -L "${clang_bin}"
-  then
-    clang_dir=`dirname "${clang_bin}"`
-    clang_bin=`readlink "${clang_bin}"`
-    llvm_bin_dir="${clang_dir}/"`dirname "${clang_bin}"`
-    llvm_path="${llvm_path}${PATH_SEPARATOR}${llvm_bin_dir}"
-  fi
-fi
 AC_SUBST(LLVM_PROFDATA)
 AC_PATH_TARGET_TOOL(LLVM_PROFDATA, llvm-profdata, '', ${llvm_path})
 AC_SUBST(LLVM_PROF_FOUND)
@@ -1526,6 +1555,7 @@ fi
 
 AC_SUBST(BASECFLAGS)
 AC_SUBST(CFLAGS_NODIST)
+AC_SUBST(LDFLAGS_NODIST)
 
 # The -arch flags for universal builds on OSX
 UNIVERSAL_ARCH_FLAGS=
@@ -2881,11 +2911,37 @@ esac
 AC_MSG_CHECKING(for --with-address-sanitizer)
 AC_ARG_WITH(address_sanitizer,
             AS_HELP_STRING([--with-address-sanitizer],
-                           [enable AddressSanitizer]),
+                           [enable AddressSanitizer (asan)]),
 [
 AC_MSG_RESULT($withval)
 BASECFLAGS="-fsanitize=address -fno-omit-frame-pointer $BASECFLAGS"
 LDFLAGS="-fsanitize=address $LDFLAGS"
+# ASan works by controlling memory allocation, our own malloc interferes.
+with_pymalloc="no"
+],
+[AC_MSG_RESULT(no)])
+
+AC_MSG_CHECKING(for --with-memory-sanitizer)
+AC_ARG_WITH(memory_sanitizer,
+            AS_HELP_STRING([--with-memory-sanitizer],
+                           [enable MemorySanitizer (msan)]),
+[
+AC_MSG_RESULT($withval)
+BASECFLAGS="-fsanitize=memory -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer $BASECFLAGS"
+LDFLAGS="-fsanitize=memory -fsanitize-memory-track-origins=2 $LDFLAGS"
+# MSan works by controlling memory allocation, our own malloc interferes.
+with_pymalloc="no"
+],
+[AC_MSG_RESULT(no)])
+
+AC_MSG_CHECKING(for --with-undefined-behavior-sanitizer)
+AC_ARG_WITH(undefined_behavior_sanitizer,
+            AS_HELP_STRING([--with-undefined-behavior-sanitizer],
+                           [enable UndefinedBehaviorSanitizer (ubsan)]),
+[
+AC_MSG_RESULT($withval)
+BASECFLAGS="-fsanitize=undefined $BASECFLAGS"
+LDFLAGS="-fsanitize=undefined $LDFLAGS"
 ],
 [AC_MSG_RESULT(no)])
 
index a97a755..b4357e3 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -18,11 +18,16 @@ from distutils.spawn import find_executable
 
 cross_compiling = "_PYTHON_HOST_PLATFORM" in os.environ
 
-# Add special CFLAGS reserved for building the interpreter and the stdlib
-# modules (Issue #21121).
-cflags = sysconfig.get_config_var('CFLAGS')
-py_cflags_nodist = sysconfig.get_config_var('PY_CFLAGS_NODIST')
-sysconfig.get_config_vars()['CFLAGS'] = cflags + ' ' + py_cflags_nodist
+# Set common compiler and linker flags derived from the Makefile,
+# reserved for building the interpreter and the stdlib modules.
+# See bpo-21121 and bpo-35257
+def set_compiler_flags(compiler_flags, compiler_py_flags_nodist):
+    flags = sysconfig.get_config_var(compiler_flags)
+    py_flags_nodist = sysconfig.get_config_var(compiler_py_flags_nodist)
+    sysconfig.get_config_vars()[compiler_flags] = flags + ' ' + py_flags_nodist
+
+set_compiler_flags('CFLAGS', 'PY_CFLAGS_NODIST')
+set_compiler_flags('LDFLAGS', 'PY_LDFLAGS_NODIST')
 
 class Dummy:
     """Hack for parallel build"""