Imported Upstream version 3.9.1 upstream/3.9.1
authorDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 9 Dec 2020 01:06:55 +0000 (10:06 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 9 Dec 2020 01:06:55 +0000 (10:06 +0900)
268 files changed:
Doc/c-api/file.rst
Doc/c-api/type.rst
Doc/data/refcounts.dat
Doc/faq/programming.rst
Doc/faq/windows.rst
Doc/glossary.rst
Doc/howto/descriptor.rst
Doc/library/argparse.rst
Doc/library/ast.rst
Doc/library/asyncio-policy.rst
Doc/library/asyncio-subprocess.rst
Doc/library/asyncio-task.rst
Doc/library/audit_events.rst
Doc/library/bz2.rst
Doc/library/collections.abc.rst
Doc/library/collections.rst
Doc/library/concurrent.futures.rst
Doc/library/ctypes.rst
Doc/library/dis.rst
Doc/library/functions.rst
Doc/library/functools.rst
Doc/library/idle.rst
Doc/library/imaplib.rst
Doc/library/importlib.rst
Doc/library/ipaddress.rst
Doc/library/itertools.rst
Doc/library/mailbox.rst
Doc/library/math.rst
Doc/library/multiprocessing.rst
Doc/library/os.rst
Doc/library/platform.rst
Doc/library/poplib.rst
Doc/library/random.rst
Doc/library/shutil.rst
Doc/library/signal.rst
Doc/library/site.rst
Doc/library/smtplib.rst
Doc/library/socket.rst
Doc/library/stdtypes.rst
Doc/library/string.rst
Doc/library/sys.rst
Doc/library/tarfile.rst
Doc/library/test.rst
Doc/library/types.rst
Doc/library/typing.rst
Doc/library/unittest.rst
Doc/library/xml.dom.minidom.rst
Doc/library/xml.etree.elementtree.rst
Doc/reference/datamodel.rst
Doc/reference/expressions.rst
Doc/reference/import.rst
Doc/reference/simple_stmts.rst
Doc/tools/extensions/c_annotations.py
Doc/tools/extensions/pyspecific.py
Doc/tools/static/switchers.js
Doc/tools/susp-ignored.csv
Doc/tools/templates/indexsidebar.html
Doc/tutorial/datastructures.rst
Doc/tutorial/errors.rst
Doc/tutorial/inputoutput.rst
Doc/using/cmdline.rst
Doc/using/venv-create.inc
Doc/using/windows.rst
Doc/whatsnew/3.8.rst
Doc/whatsnew/3.9.rst
Grammar/python.gram
Include/cpython/fileobject.h
Include/fileobject.h
Include/internal/pycore_byteswap.h
Include/internal/pycore_pylifecycle.h
Include/patchlevel.h
Include/pyport.h
Lib/_osx_support.py
Lib/ast.py
Lib/asyncio/base_futures.py
Lib/asyncio/exceptions.py
Lib/asyncio/tasks.py
Lib/binhex.py
Lib/cProfile.py
Lib/collections/__init__.py
Lib/copyreg.py
Lib/ctypes/macholib/dyld.py
Lib/ctypes/test/test_find.py
Lib/ctypes/test/test_macholib.py
Lib/ctypes/test/test_wintypes.py
Lib/ctypes/util.py
Lib/datetime.py
Lib/distutils/spawn.py
Lib/distutils/tests/test_build_ext.py
Lib/distutils/unixccompiler.py
Lib/email/generator.py
Lib/ensurepip/_bundled/pip-20.2.1-py2.py3-none-any.whl [deleted file]
Lib/graphlib.py
Lib/http/server.py
Lib/idlelib/Icons/README.txt
Lib/idlelib/Icons/idle.ico
Lib/idlelib/NEWS.txt
Lib/idlelib/calltip.py
Lib/idlelib/configdialog.py
Lib/idlelib/help.html
Lib/idlelib/help.py
Lib/idlelib/idle_test/mock_tk.py
Lib/idlelib/idle_test/test_calltip.py
Lib/idlelib/idle_test/test_searchbase.py
Lib/idlelib/idle_test/test_searchengine.py
Lib/idlelib/pyshell.py
Lib/idlelib/runscript.py
Lib/idlelib/searchbase.py
Lib/idlelib/searchengine.py
Lib/idlelib/statusbar.py
Lib/inspect.py
Lib/logging/__init__.py
Lib/pickle.py
Lib/plistlib.py
Lib/profile.py
Lib/pstats.py
Lib/pydoc_data/topics.py
Lib/shutil.py
Lib/subprocess.py
Lib/symtable.py
Lib/tarfile.py
Lib/test/datetimetester.py
Lib/test/inspect_fodder.py
Lib/test/multibytecodec_support.py
Lib/test/pickletester.py
Lib/test/test__locale.py
Lib/test/test_asyncio/test_events.py
Lib/test/test_asyncio/test_futures2.py [new file with mode: 0644]
Lib/test/test_asyncio/test_streams.py
Lib/test/test_asyncio/test_tasks.py
Lib/test/test_binhex.py
Lib/test/test_bytes.py
Lib/test/test_clinic.py
Lib/test/test_codecs.py
Lib/test/test_collections.py
Lib/test/test_email/test_email.py
Lib/test/test_exceptions.py
Lib/test/test_finalization.py
Lib/test/test_format.py
Lib/test/test_gc.py
Lib/test/test_gdb.py
Lib/test/test_genericalias.py
Lib/test/test_hashlib.py
Lib/test/test_httpservers.py
Lib/test/test_inspect.py
Lib/test/test_isinstance.py
Lib/test/test_itertools.py
Lib/test/test_lib2to3.py
Lib/test/test_logging.py
Lib/test/test_named_expressions.py
Lib/test/test_peepholer.py
Lib/test/test_peg_generator/test_c_parser.py
Lib/test/test_platform.py
Lib/test/test_plistlib.py
Lib/test/test_posix.py
Lib/test/test_profile.py
Lib/test/test_pstats.py
Lib/test/test_shutil.py
Lib/test/test_site.py
Lib/test/test_ssl.py
Lib/test/test_subprocess.py
Lib/test/test_symtable.py
Lib/test/test_syntax.py
Lib/test/test_tarfile.py
Lib/test/test_threading.py
Lib/test/test_time.py
Lib/test/test_traceback.py
Lib/test/test_typing.py
Lib/test/test_unicode.py
Lib/test/test_unparse.py
Lib/test/test_urllib2.py
Lib/test/test_with.py
Lib/test/test_xml_etree.py
Lib/test/test_zipfile.py
Lib/test/test_zoneinfo/test_zoneinfo.py
Lib/threading.py
Lib/tkinter/__init__.py
Lib/tkinter/test/test_tkinter/test_misc.py
Lib/tkinter/test/test_tkinter/test_widgets.py
Lib/tkinter/test/test_ttk/test_extensions.py
Lib/tkinter/test/test_ttk/test_functions.py
Lib/tkinter/test/test_ttk/test_style.py
Lib/tkinter/test/test_ttk/test_widgets.py
Lib/tkinter/ttk.py
Lib/traceback.py
Lib/typing.py
Lib/urllib/request.py
Lib/webbrowser.py
Lib/xml/etree/ElementTree.py
Lib/zipfile.py
Mac/BuildScript/build-installer.py
Mac/BuildScript/openssl-mac-arm64.patch [new file with mode: 0644]
Mac/BuildScript/resources/ReadMe.rtf
Mac/README.rst
Mac/Tools/pythonw.c
Makefile.pre.in
Misc/ACKS
Misc/NEWS
Modules/_ctypes/callbacks.c
Modules/_ctypes/callproc.c
Modules/_ctypes/cfield.c
Modules/_ctypes/ctypes.h
Modules/_ctypes/malloc_closure.c
Modules/_decimal/libmpdec/mpdecimal.h
Modules/_localemodule.c
Modules/_operator.c
Modules/_posixsubprocess.c
Modules/_randommodule.c
Modules/_ssl.c
Modules/_testcapimodule.c
Modules/_tracemalloc.c
Modules/clinic/_randommodule.c.h
Modules/getpath.c
Modules/posixmodule.c
Modules/signalmodule.c
Modules/timemodule.c
Objects/abstract.c
Objects/funcobject.c
Objects/genericaliasobject.c
Objects/methodobject.c
Objects/typeobject.c
Objects/unicodeobject.c
PC/icons/idlex150.png [new file with mode: 0644]
PC/icons/idlex44.png [new file with mode: 0644]
PC/layout/support/appxmanifest.py
PC/pyconfig.h
PCbuild/build.bat
PCbuild/env.bat
PCbuild/env.ps1 [new file with mode: 0644]
PCbuild/get_externals.bat
PCbuild/idle.bat
PCbuild/prepare_libffi.bat
PCbuild/pyproject.props
PCbuild/python.props
PCbuild/readme.txt
PCbuild/rt.bat
Parser/pegen/parse.c
Parser/pegen/parse_string.c
Parser/pegen/pegen.c
Parser/pegen/pegen.h
Python/_warnings.c
Python/bootstrap_hash.c
Python/ceval.c
Python/compile.c
Python/dynload_aix.c
Python/dynload_hpux.c
Python/dynload_shlib.c
Python/fileutils.c
Python/import.c
Python/initconfig.c
Python/peephole.c
Python/pylifecycle.c
Python/pytime.c
README.rst
Tools/c-analyzer/c_analyzer/common/files.py
Tools/clinic/clinic.py
Tools/msi/sdktools.psm1
Tools/msi/test/test_files.wxs
Tools/nuget/python.nuspec
Tools/nuget/pythonarm32.nuspec
Tools/nuget/pythondaily.nuspec
Tools/nuget/pythonx86.nuspec
Tools/peg_generator/pegen/c_generator.py
Tools/peg_generator/scripts/test_parse_directory.py
configure
configure.ac
pyconfig.h.in
setup.py

index ea027ee..ed3735a 100644 (file)
@@ -82,6 +82,8 @@ the :mod:`io` APIs instead.
 
    This function is safe to call before :c:func:`Py_Initialize`.
 
+   .. audit-event:: setopencodehook "" c.PyFile_SetOpenCodeHook
+
    .. versionadded:: 3.8
 
 
index 73f2687..822360e 100644 (file)
@@ -155,7 +155,8 @@ The following functions and structs are used to create
    If *bases* is a tuple, the created heap type contains all types contained
    in it as base types.
 
-   If *bases* is ``NULL``, the *Py_tp_base* slot is used instead.
+   If *bases* is ``NULL``, the *Py_tp_bases* slot is used instead.
+   If that also is ``NULL``, the *Py_tp_base* slot is used instead.
    If that also is ``NULL``, the new type derives from :class:`object`.
 
    The *module* argument can be used to record the module in which the new
@@ -247,7 +248,8 @@ The following functions and structs are used to create
       * :c:member:`~PyBufferProcs.bf_getbuffer`
       * :c:member:`~PyBufferProcs.bf_releasebuffer`
 
-      Setting :c:data:`Py_tp_bases` may be problematic on some platforms.
+      Setting :c:data:`Py_tp_bases` or :c:data:`Py_tp_base` may be
+      problematic on some platforms.
       To avoid issues, use the *bases* argument of
       :py:func:`PyType_FromSpecWithBases` instead.
 
index 4dacbe2..aac1351 100644 (file)
@@ -2306,6 +2306,11 @@ PyType_CheckExact:PyObject*:o:0:
 PyType_FromSpec:PyObject*::+1:
 PyType_FromSpec:PyType_Spec*:spec::
 
+PyType_FromModuleAndSpec:PyObject*::+1:
+PyType_FromModuleAndSpec:PyObject*:module:+1:
+PyType_FromModuleAndSpec:PyType_Spec*:spec::
+PyType_FromModuleAndSpec:PyObject*:bases:0:
+
 PyType_FromSpecWithBases:PyObject*::+1:
 PyType_FromSpecWithBases:PyType_Spec*:spec::
 PyType_FromSpecWithBases:PyObject*:bases:0:
index 66d210a..8df62c5 100644 (file)
@@ -942,7 +942,7 @@ There are various techniques.
      f()
 
 
-* Use :func:`locals` or :func:`eval` to resolve the function name::
+* Use :func:`locals` to resolve the function name::
 
      def myFunc():
          print("hello")
@@ -952,12 +952,6 @@ There are various techniques.
      f = locals()[fname]
      f()
 
-     f = eval(fname)
-     f()
-
-  Note: Using :func:`eval` is slow and dangerous.  If you don't have absolute
-  control over the contents of the string, someone could pass a string that
-  resulted in an arbitrary function being executed.
 
 Is there an equivalent to Perl's chomp() for removing trailing newlines from strings?
 -------------------------------------------------------------------------------------
@@ -1122,7 +1116,7 @@ trailing newline from a string.
 How do I iterate over a sequence in reverse order?
 --------------------------------------------------
 
-Use the :func:`reversed` built-in function, which is new in Python 2.4::
+Use the :func:`reversed` built-in function::
 
    for x in reversed(sequence):
        ...  # do something with x ...
@@ -1130,11 +1124,6 @@ Use the :func:`reversed` built-in function, which is new in Python 2.4::
 This won't touch your original sequence, but build a new copy with reversed
 order to iterate over.
 
-With Python 2.3, you can use an extended slice syntax::
-
-   for x in sequence[::-1]:
-       ...  # do something with x ...
-
 
 How do you remove duplicates from a list?
 -----------------------------------------
@@ -1164,6 +1153,21 @@ This converts the list into a set, thereby removing duplicates, and then back
 into a list.
 
 
+How do you remove multiple items from a list
+--------------------------------------------
+
+As with removing duplicates, explicitly iterating in reverse with a
+delete condition is one possibility.  However, it is easier and faster
+to use slice replacement with an implicit or explicit forward iteration.
+Here are three variations.::
+
+   mylist[:] = filter(keep_function, mylist)
+   mylist[:] = (x for x in mylist if keep_condition)
+   mylist[:] = [x for x in mylist if keep_condition]
+
+The list comprehension may be fastest.
+
+
 How do you make an array in Python?
 -----------------------------------
 
@@ -1366,20 +1370,6 @@ out the element you want. ::
    ['else', 'sort', 'to', 'something']
 
 
-An alternative for the last step is::
-
-   >>> result = []
-   >>> for p in pairs: result.append(p[1])
-
-If you find this more legible, you might prefer to use this instead of the final
-list comprehension.  However, it is almost twice as slow for long lists.  Why?
-First, the ``append()`` operation has to reallocate memory, and while it uses
-some tricks to avoid doing that each time, it still has to do it occasionally,
-and that costs quite a bit.  Second, the expression "result.append" requires an
-extra attribute lookup, and third, there's a speed reduction from having to make
-all those function calls.
-
-
 Objects
 =======
 
@@ -1523,18 +1513,18 @@ provide the ``self`` argument.
 How can I organize my code to make it easier to change the base class?
 ----------------------------------------------------------------------
 
-You could define an alias for the base class, assign the real base class to it
-before your class definition, and use the alias throughout your class.  Then all
+You could assign the base class to an alias and derive from the alias.  Then all
 you have to change is the value assigned to the alias.  Incidentally, this trick
 is also handy if you want to decide dynamically (e.g. depending on availability
 of resources) which base class to use.  Example::
 
-   BaseAlias = <real base class>
+   class Base:
+       ...
+
+   BaseAlias = Base
 
    class Derived(BaseAlias):
-       def meth(self):
-           BaseAlias.meth(self)
-           ...
+       ...
 
 
 How do I create static class data and static class methods?
index a181086..c8e9c5f 100644 (file)
@@ -140,7 +140,7 @@ offender.
 How do I make an executable from a Python script?
 -------------------------------------------------
 
-See `cx_Freeze <https://anthony-tuininga.github.io/cx_Freeze/>`_ for a distutils extension
+See `cx_Freeze <https://cx-freeze.readthedocs.io/en/latest/>`_ for a distutils extension
 that allows you to create console and GUI executables from Python code.
 `py2exe <http://www.py2exe.org/>`_, the most popular extension for building
 Python 2.x-based executables, does not yet support Python 3 but a version that
@@ -279,7 +279,7 @@ in batch mode.
 How do I check for a keypress without blocking?
 -----------------------------------------------
 
-Use the msvcrt module.  This is a standard Windows-specific extension module.
+Use the :mod:`msvcrt` module.  This is a standard Windows-specific extension module.
 It defines a function ``kbhit()`` which checks whether a keyboard hit is
 present, and ``getch()`` which gets one character without echoing it.
 
index 9fdbdb1..4fd01e0 100644 (file)
@@ -301,13 +301,20 @@ Glossary
       including functions, methods, properties, class methods, static methods,
       and reference to super classes.
 
-      For more information about descriptors' methods, see :ref:`descriptors`.
+      For more information about descriptors' methods, see :ref:`descriptors`
+      or the :ref:`Descriptor How To Guide <descriptorhowto>`.
 
    dictionary
       An associative array, where arbitrary keys are mapped to values.  The
       keys can be any object with :meth:`__hash__` and :meth:`__eq__` methods.
       Called a hash in Perl.
 
+   dictionary comprehension
+      A compact way to process all or part of the elements in an iterable and
+      return a dictionary with the results. ``results = {n: n ** 2 for n in
+      range(10)}`` generates a dictionary containing key ``n`` mapped to
+      value ``n ** 2``. See :ref:`comprehensions`.
+
    dictionary view
       The objects returned from :meth:`dict.keys`, :meth:`dict.values`, and
       :meth:`dict.items` are called dictionary views. They provide a dynamic
@@ -476,6 +483,13 @@ Glossary
       See also the :term:`single dispatch` glossary entry, the
       :func:`functools.singledispatch` decorator, and :pep:`443`.
 
+   generic type
+      A :term:`type` that can be parameterized; typically a container like
+      :class:`list`. Used for :term:`type hints <type hint>` and
+      :term:`annotations <annotation>`.
+
+      See :pep:`483` for more details, and :mod:`typing` or
+      :ref:`generic alias type <types-genericalias>` for its uses.
 
    GIL
       See :term:`global interpreter lock`.
@@ -1024,7 +1038,13 @@ Glossary
       :meth:`index`, :meth:`__contains__`, and
       :meth:`__reversed__`. Types that implement this expanded
       interface can be registered explicitly using
-      :func:`~abc.register`.
+      :func:`~abc.ABCMeta.register`.
+
+   set comprehension
+      A compact way to process all or part of the elements in an iterable and
+      return a set with the results. ``results = {c for c in 'abracadabra' if
+      c not in 'abc'}`` generates the set of strings ``{'r', 'd'}``.  See
+      :ref:`comprehensions`.
 
    single dispatch
       A form of :term:`generic function` dispatch where the implementation is
index b792b6c..ab5a573 100644 (file)
@@ -1,3 +1,5 @@
+.. _descriptorhowto:
+
 ======================
 Descriptor HowTo Guide
 ======================
@@ -7,45 +9,514 @@ Descriptor HowTo Guide
 
 .. Contents::
 
+
+:term:`Descriptors <descriptor>` let objects customize attribute lookup,
+storage, and deletion.
+
+This guide has four major sections:
+
+1) The "primer" gives a basic overview, moving gently from simple examples,
+   adding one feature at a time.  Start here if you're new to descriptors.
+
+2) The second section shows a complete, practical descriptor example.  If you
+   already know the basics, start there.
+
+3) The third section provides a more technical tutorial that goes into the
+   detailed mechanics of how descriptors work.  Most people don't need this
+   level of detail.
+
+4) The last section has pure Python equivalents for built-in descriptors that
+   are written in C.  Read this if you're curious about how functions turn
+   into bound methods or about the implementation of common tools like
+   :func:`classmethod`, :func:`staticmethod`, :func:`property`, and
+   :term:`__slots__`.
+
+
+Primer
+^^^^^^
+
+In this primer, we start with the most basic possible example and then we'll
+add new capabilities one by one.
+
+
+Simple example: A descriptor that returns a constant
+----------------------------------------------------
+
+The :class:`Ten` class is a descriptor that always returns the constant ``10``
+from its :meth:`__get__` method:
+
+.. testcode::
+
+    class Ten:
+        def __get__(self, obj, objtype=None):
+            return 10
+
+To use the descriptor, it must be stored as a class variable in another class:
+
+.. testcode::
+
+    class A:
+        x = 5                       # Regular class attribute
+        y = Ten()                   # Descriptor instance
+
+An interactive session shows the difference between normal attribute lookup
+and descriptor lookup:
+
+.. doctest::
+
+    >>> a = A()                     # Make an instance of class A
+    >>> a.x                         # Normal attribute lookup
+    5
+    >>> a.y                         # Descriptor lookup
+    10
+
+In the ``a.x`` attribute lookup, the dot operator finds the key ``x`` and the
+value ``5`` in the class dictionary.  In the ``a.y`` lookup, the dot operator
+finds a descriptor instance, recognized by its ``__get__`` method, and calls
+that method which returns ``10``.
+
+Note that the value ``10`` is not stored in either the class dictionary or the
+instance dictionary.  Instead, the value ``10`` is computed on demand.
+
+This example shows how a simple descriptor works, but it isn't very useful.
+For retrieving constants, normal attribute lookup would be better.
+
+In the next section, we'll create something more useful, a dynamic lookup.
+
+
+Dynamic lookups
+---------------
+
+Interesting descriptors typically run computations instead of returning
+constants:
+
+.. testcode::
+
+    import os
+
+    class DirectorySize:
+
+        def __get__(self, obj, objtype=None):
+            return len(os.listdir(obj.dirname))
+
+    class Directory:
+
+        size = DirectorySize()              # Descriptor instance
+
+        def __init__(self, dirname):
+            self.dirname = dirname          # Regular instance attribute
+
+An interactive session shows that the lookup is dynamic — it computes
+different, updated answers each time::
+
+    >>> s = Directory('songs')
+    >>> g = Directory('games')
+    >>> s.size                              # The songs directory has twenty files
+    20
+    >>> g.size                              # The games directory has three files
+    3
+    >>> open('games/newfile').close()       # Add a fourth file to the directory
+    >>> g.size                              # File count is automatically updated
+    4
+
+Besides showing how descriptors can run computations, this example also
+reveals the purpose of the parameters to :meth:`__get__`.  The *self*
+parameter is *size*, an instance of *DirectorySize*.  The *obj* parameter is
+either *g* or *s*, an instance of *Directory*.  It is the *obj* parameter that
+lets the :meth:`__get__` method learn the target directory.  The *objtype*
+parameter is the class *Directory*.
+
+
+Managed attributes
+------------------
+
+A popular use for descriptors is managing access to instance data.  The
+descriptor is assigned to a public attribute in the class dictionary while the
+actual data is stored as a private attribute in the instance dictionary.  The
+descriptor's :meth:`__get__` and :meth:`__set__` methods are triggered when
+the public attribute is accessed.
+
+In the following example, *age* is the public attribute and *_age* is the
+private attribute.  When the public attribute is accessed, the descriptor logs
+the lookup or update:
+
+.. testcode::
+
+    import logging
+
+    logging.basicConfig(level=logging.INFO)
+
+    class LoggedAgeAccess:
+
+        def __get__(self, obj, objtype=None):
+            value = obj._age
+            logging.info('Accessing %r giving %r', 'age', value)
+            return value
+
+        def __set__(self, obj, value):
+            logging.info('Updating %r to %r', 'age', value)
+            obj._age = value
+
+    class Person:
+
+        age = LoggedAgeAccess()             # Descriptor instance
+
+        def __init__(self, name, age):
+            self.name = name                # Regular instance attribute
+            self.age = age                  # Calls __set__()
+
+        def birthday(self):
+            self.age += 1                   # Calls both __get__() and __set__()
+
+
+An interactive session shows that all access to the managed attribute *age* is
+logged, but that the regular attribute *name* is not logged:
+
+.. testcode::
+    :hide:
+
+    import logging, sys
+    logging.basicConfig(level=logging.INFO, stream=sys.stdout, force=True)
+
+.. doctest::
+
+    >>> mary = Person('Mary M', 30)         # The initial age update is logged
+    INFO:root:Updating 'age' to 30
+    >>> dave = Person('David D', 40)
+    INFO:root:Updating 'age' to 40
+
+    >>> vars(mary)                          # The actual data is in a private attribute
+    {'name': 'Mary M', '_age': 30}
+    >>> vars(dave)
+    {'name': 'David D', '_age': 40}
+
+    >>> mary.age                            # Access the data and log the lookup
+    INFO:root:Accessing 'age' giving 30
+    30
+    >>> mary.birthday()                     # Updates are logged as well
+    INFO:root:Accessing 'age' giving 30
+    INFO:root:Updating 'age' to 31
+
+    >>> dave.name                           # Regular attribute lookup isn't logged
+    'David D'
+    >>> dave.age                            # Only the managed attribute is logged
+    INFO:root:Accessing 'age' giving 40
+    40
+
+One major issue with this example is that the private name *_age* is hardwired in
+the *LoggedAgeAccess* class.  That means that each instance can only have one
+logged attribute and that its name is unchangeable.  In the next example,
+we'll fix that problem.
+
+
+Customized names
+----------------
+
+When a class uses descriptors, it can inform each descriptor about which
+variable name was used.
+
+In this example, the :class:`Person` class has two descriptor instances,
+*name* and *age*.  When the :class:`Person` class is defined, it makes a
+callback to :meth:`__set_name__` in *LoggedAccess* so that the field names can
+be recorded, giving each descriptor its own *public_name* and *private_name*:
+
+.. testcode::
+
+    import logging
+
+    logging.basicConfig(level=logging.INFO)
+
+    class LoggedAccess:
+
+        def __set_name__(self, owner, name):
+            self.public_name = name
+            self.private_name = '_' + name
+
+        def __get__(self, obj, objtype=None):
+            value = getattr(obj, self.private_name)
+            logging.info('Accessing %r giving %r', self.public_name, value)
+            return value
+
+        def __set__(self, obj, value):
+            logging.info('Updating %r to %r', self.public_name, value)
+            setattr(obj, self.private_name, value)
+
+    class Person:
+
+        name = LoggedAccess()                # First descriptor instance
+        age = LoggedAccess()                 # Second descriptor instance
+
+        def __init__(self, name, age):
+            self.name = name                 # Calls the first descriptor
+            self.age = age                   # Calls the second descriptor
+
+        def birthday(self):
+            self.age += 1
+
+An interactive session shows that the :class:`Person` class has called
+:meth:`__set_name__` so that the field names would be recorded.  Here
+we call :func:`vars` to look up the descriptor without triggering it:
+
+.. doctest::
+
+    >>> vars(vars(Person)['name'])
+    {'public_name': 'name', 'private_name': '_name'}
+    >>> vars(vars(Person)['age'])
+    {'public_name': 'age', 'private_name': '_age'}
+
+The new class now logs access to both *name* and *age*:
+
+.. testcode::
+    :hide:
+
+    import logging, sys
+    logging.basicConfig(level=logging.INFO, stream=sys.stdout, force=True)
+
+.. doctest::
+
+    >>> pete = Person('Peter P', 10)
+    INFO:root:Updating 'name' to 'Peter P'
+    INFO:root:Updating 'age' to 10
+    >>> kate = Person('Catherine C', 20)
+    INFO:root:Updating 'name' to 'Catherine C'
+    INFO:root:Updating 'age' to 20
+
+The two *Person* instances contain only the private names::
+
+    >>> vars(pete)
+    {'_name': 'Peter P', '_age': 10}
+    >>> vars(kate)
+    {'_name': 'Catherine C', '_age': 20}
+
+
+Closing thoughts
+----------------
+
+A :term:`descriptor` is what we call any object that defines :meth:`__get__`,
+:meth:`__set__`, or :meth:`__delete__`.
+
+Optionally, descriptors can have a :meth:`__set_name__` method.  This is only
+used in cases where a descriptor needs to know either the class where it was
+created or the name of class variable it was assigned to.  (This method, if
+present, is called even if the class is not a descriptor.)
+
+Descriptors get invoked by the dot "operator" during attribute lookup.  If a
+descriptor is accessed indirectly with ``vars(some_class)[descriptor_name]``,
+the descriptor instance is returned without invoking it.
+
+Descriptors only work when used as class variables.  When put in instances,
+they have no effect.
+
+The main motivation for descriptors is to provide a hook allowing objects
+stored in class variables to control what happens during attribute lookup.
+
+Traditionally, the calling class controls what happens during lookup.
+Descriptors invert that relationship and allow the data being looked-up to
+have a say in the matter.
+
+Descriptors are used throughout the language.  It is how functions turn into
+bound methods.  Common tools like :func:`classmethod`, :func:`staticmethod`,
+:func:`property`, and :func:`functools.cached_property` are all implemented as
+descriptors.
+
+
+Complete Practical Example
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In this example, we create a practical and powerful tool for locating
+notoriously hard to find data corruption bugs.
+
+
+Validator class
+---------------
+
+A validator is a descriptor for managed attribute access.  Prior to storing
+any data, it verifies that the new value meets various type and range
+restrictions.  If those restrictions aren't met, it raises an exception to
+prevent data corruption at its source.
+
+This :class:`Validator` class is both an :term:`abstract base class` and a
+managed attribute descriptor:
+
+.. testcode::
+
+    from abc import ABC, abstractmethod
+
+    class Validator(ABC):
+
+        def __set_name__(self, owner, name):
+            self.private_name = '_' + name
+
+        def __get__(self, obj, objtype=None):
+            return getattr(obj, self.private_name)
+
+        def __set__(self, obj, value):
+            self.validate(value)
+            setattr(obj, self.private_name, value)
+
+        @abstractmethod
+        def validate(self, value):
+            pass
+
+Custom validators need to inherit from :class:`Validator` and must supply a
+:meth:`validate` method to test various restrictions as needed.
+
+
+Custom validators
+-----------------
+
+Here are three practical data validation utilities:
+
+1) :class:`OneOf` verifies that a value is one of a restricted set of options.
+
+2) :class:`Number` verifies that a value is either an :class:`int` or
+   :class:`float`.  Optionally, it verifies that a value is between a given
+   minimum or maximum.
+
+3) :class:`String` verifies that a value is a :class:`str`.  Optionally, it
+   validates a given minimum or maximum length.  It can validate a
+   user-defined `predicate
+   <https://en.wikipedia.org/wiki/Predicate_(mathematical_logic)>`_ as well.
+
+.. testcode::
+
+    class OneOf(Validator):
+
+        def __init__(self, *options):
+            self.options = set(options)
+
+        def validate(self, value):
+            if value not in self.options:
+                raise ValueError(f'Expected {value!r} to be one of {self.options!r}')
+
+    class Number(Validator):
+
+        def __init__(self, minvalue=None, maxvalue=None):
+            self.minvalue = minvalue
+            self.maxvalue = maxvalue
+
+        def validate(self, value):
+            if not isinstance(value, (int, float)):
+                raise TypeError(f'Expected {value!r} to be an int or float')
+            if self.minvalue is not None and value < self.minvalue:
+                raise ValueError(
+                    f'Expected {value!r} to be at least {self.minvalue!r}'
+                )
+            if self.maxvalue is not None and value > self.maxvalue:
+                raise ValueError(
+                    f'Expected {value!r} to be no more than {self.maxvalue!r}'
+                )
+
+    class String(Validator):
+
+        def __init__(self, minsize=None, maxsize=None, predicate=None):
+            self.minsize = minsize
+            self.maxsize = maxsize
+            self.predicate = predicate
+
+        def validate(self, value):
+            if not isinstance(value, str):
+                raise TypeError(f'Expected {value!r} to be an str')
+            if self.minsize is not None and len(value) < self.minsize:
+                raise ValueError(
+                    f'Expected {value!r} to be no smaller than {self.minsize!r}'
+                )
+            if self.maxsize is not None and len(value) > self.maxsize:
+                raise ValueError(
+                    f'Expected {value!r} to be no bigger than {self.maxsize!r}'
+                )
+            if self.predicate is not None and not self.predicate(value):
+                raise ValueError(
+                    f'Expected {self.predicate} to be true for {value!r}'
+                )
+
+
+Practical application
+---------------------
+
+Here's how the data validators can be used in a real class:
+
+.. testcode::
+
+    class Component:
+
+        name = String(minsize=3, maxsize=10, predicate=str.isupper)
+        kind = OneOf('wood', 'metal', 'plastic')
+        quantity = Number(minvalue=0)
+
+        def __init__(self, name, kind, quantity):
+            self.name = name
+            self.kind = kind
+            self.quantity = quantity
+
+The descriptors prevent invalid instances from being created:
+
+.. doctest::
+
+    >>> Component('Widget', 'metal', 5)      # Blocked: 'Widget' is not all uppercase
+    Traceback (most recent call last):
+        ...
+    ValueError: Expected <method 'isupper' of 'str' objects> to be true for 'Widget'
+
+    >>> Component('WIDGET', 'metle', 5)      # Blocked: 'metle' is misspelled
+    Traceback (most recent call last):
+        ...
+    ValueError: Expected 'metle' to be one of {'metal', 'plastic', 'wood'}
+
+    >>> Component('WIDGET', 'metal', -5)     # Blocked: -5 is negative
+    Traceback (most recent call last):
+        ...
+    ValueError: Expected -5 to be at least 0
+    >>> Component('WIDGET', 'metal', 'V')    # Blocked: 'V' isn't a number
+    Traceback (most recent call last):
+        ...
+    TypeError: Expected 'V' to be an int or float
+
+    >>> c = Component('WIDGET', 'metal', 5)  # Allowed:  The inputs are valid
+
+
+Technical Tutorial
+^^^^^^^^^^^^^^^^^^
+
+What follows is a more technical tutorial for the mechanics and details of how
+descriptors work.
+
+
 Abstract
 --------
 
 Defines descriptors, summarizes the protocol, and shows how descriptors are
-called.  Examines a custom descriptor and several built-in Python descriptors
-including functions, properties, static methods, and class methods.  Shows how
-each works by giving a pure Python equivalent and a sample application.
+called.  Provides an example showing how object relational mappings work.
 
 Learning about descriptors not only provides access to a larger toolset, it
-creates a deeper understanding of how Python works and an appreciation for the
-elegance of its design.
+creates a deeper understanding of how Python works.
 
 
-Definition and Introduction
+Definition and introduction
 ---------------------------
 
-In general, a descriptor is an object attribute with "binding behavior", one
-whose attribute access has been overridden by methods in the descriptor
-protocol.  Those methods are :meth:`__get__`, :meth:`__set__`, and
-:meth:`__delete__`.  If any of those methods are defined for an object, it is
-said to be a descriptor.
+In general, a descriptor is an attribute value that has one of the methods in
+the descriptor protocol.  Those methods are :meth:`__get__`, :meth:`__set__`,
+and :meth:`__delete__`.  If any of those methods are defined for an the
+attribute, it is said to be a :term:`descriptor`.
 
 The default behavior for attribute access is to get, set, or delete the
 attribute from an object's dictionary.  For instance, ``a.x`` has a lookup chain
 starting with ``a.__dict__['x']``, then ``type(a).__dict__['x']``, and
-continuing through the base classes of ``type(a)`` excluding metaclasses. If the
+continuing through the method resolution order of ``type(a)``. If the
 looked-up value is an object defining one of the descriptor methods, then Python
 may override the default behavior and invoke the descriptor method instead.
 Where this occurs in the precedence chain depends on which descriptor methods
 were defined.
 
 Descriptors are a powerful, general purpose protocol.  They are the mechanism
-behind properties, methods, static methods, class methods, and :func:`super()`.
-They are used throughout Python itself to implement the new style classes
-introduced in version 2.2.  Descriptors simplify the underlying C-code and offer
-a flexible set of new tools for everyday Python programs.
+behind properties, methods, static methods, class methods, and
+:func:`super()`.  They are used throughout Python itself.  Descriptors
+simplify the underlying C code and offer a flexible set of new tools for
+everyday Python programs.
 
 
-Descriptor Protocol
+Descriptor protocol
 -------------------
 
 ``descr.__get__(self, obj, type=None) -> value``
@@ -60,7 +531,7 @@ as an attribute.
 
 If an object defines :meth:`__set__` or :meth:`__delete__`, it is considered
 a data descriptor.  Descriptors that only define :meth:`__get__` are called
-non-data descriptors (they are typically used for methods but other uses are
+non-data descriptors (they are often used for methods but other uses are
 possible).
 
 Data and non-data descriptors differ in how overrides are calculated with
@@ -75,124 +546,372 @@ called.  Defining the :meth:`__set__` method with an exception raising
 placeholder is enough to make it a data descriptor.
 
 
-Invoking Descriptors
---------------------
+Overview of descriptor invocation
+---------------------------------
+
+A descriptor can be called directly with ``desc.__get__(obj)`` or
+``desc.__get__(None, cls)``.
+
+But it is more common for a descriptor to be invoked automatically from
+attribute access.
+
+The expression ``obj.x`` looks up the attribute ``x`` in the chain of
+namespaces for ``obj``.  If the search finds a descriptor outside of the
+instance ``__dict__``, its :meth:`__get__` method is invoked according to the
+precedence rules listed below.
+
+The details of invocation depend on whether ``obj`` is an object, class, or
+instance of super.
+
+
+Invocation from an instance
+---------------------------
+
+Instance lookup scans through a chain of namespaces giving data descriptors
+the highest priority, followed by instance variables, then non-data
+descriptors, then class variables, and lastly :meth:`__getattr__` if it is
+provided.
 
-A descriptor can be called directly by its method name.  For example,
-``d.__get__(obj)``.
+If a descriptor is found for ``a.x``, then it is invoked with:
+``desc.__get__(a, type(a))``.
 
-Alternatively, it is more common for a descriptor to be invoked automatically
-upon attribute access.  For example, ``obj.d`` looks up ``d`` in the dictionary
-of ``obj``.  If ``d`` defines the method :meth:`__get__`, then ``d.__get__(obj)``
-is invoked according to the precedence rules listed below.
+The logic for a dotted lookup is in :meth:`object.__getattribute__`.  Here is
+a pure Python equivalent:
 
-The details of invocation depend on whether ``obj`` is an object or a class.
+.. testcode::
+
+    def object_getattribute(obj, name):
+        "Emulate PyObject_GenericGetAttr() in Objects/object.c"
+        null = object()
+        objtype = type(obj)
+        cls_var = getattr(objtype, name, null)
+        descr_get = getattr(type(cls_var), '__get__', null)
+        if descr_get is not null:
+            if (hasattr(type(cls_var), '__set__')
+                or hasattr(type(cls_var), '__delete__')):
+                return descr_get(cls_var, obj, objtype)     # data descriptor
+        if hasattr(obj, '__dict__') and name in vars(obj):
+            return vars(obj)[name]                          # instance variable
+        if descr_get is not null:
+            return descr_get(cls_var, obj, objtype)         # non-data descriptor
+        if cls_var is not null:
+            return cls_var                                  # class variable
+        raise AttributeError(name)
 
-For objects, the machinery is in :meth:`object.__getattribute__` which
-transforms ``b.x`` into ``type(b).__dict__['x'].__get__(b, type(b))``.  The
-implementation works through a precedence chain that gives data descriptors
-priority over instance variables, instance variables priority over non-data
-descriptors, and assigns lowest priority to :meth:`__getattr__` if provided.
-The full C implementation can be found in :c:func:`PyObject_GenericGetAttr()` in
-:source:`Objects/object.c`.
 
-For classes, the machinery is in :meth:`type.__getattribute__` which transforms
-``B.x`` into ``B.__dict__['x'].__get__(None, B)``.  In pure Python, it looks
-like::
+.. testcode::
+    :hide:
 
-    def __getattribute__(self, key):
-        "Emulate type_getattro() in Objects/typeobject.c"
-        v = object.__getattribute__(self, key)
-        if hasattr(v, '__get__'):
-            return v.__get__(None, self)
-        return v
+    # Test the fidelity of object_getattribute() by comparing it with the
+    # normal object.__getattribute__().  The former will be accessed by
+    # square brackets and the latter by the dot operator.
+
+    class Object:
+
+        def __getitem__(obj, name):
+            try:
+                return object_getattribute(obj, name)
+            except AttributeError:
+                if not hasattr(type(obj), '__getattr__'):
+                    raise
+            return type(obj).__getattr__(obj, name)             # __getattr__
+
+    class DualOperator(Object):
+
+        x = 10
+
+        def __init__(self, z):
+            self.z = z
+
+        @property
+        def p2(self):
+            return 2 * self.x
+
+        @property
+        def p3(self):
+            return 3 * self.x
+
+        def m5(self, y):
+            return 5 * y
+
+        def m7(self, y):
+            return 7 * y
+
+        def __getattr__(self, name):
+            return ('getattr_hook', self, name)
+
+    class DualOperatorWithSlots:
+
+        __getitem__ = Object.__getitem__
+
+        __slots__ = ['z']
+
+        x = 15
+
+        def __init__(self, z):
+            self.z = z
+
+        @property
+        def p2(self):
+            return 2 * self.x
+
+        def m5(self, y):
+            return 5 * y
+
+        def __getattr__(self, name):
+            return ('getattr_hook', self, name)
+
+
+.. doctest::
+    :hide:
+
+    >>> a = DualOperator(11)
+    >>> vars(a).update(p3 = '_p3', m7 = '_m7')
+    >>> a.x == a['x'] == 10
+    True
+    >>> a.z == a['z'] == 11
+    True
+    >>> a.p2 == a['p2'] == 20
+    True
+    >>> a.p3 == a['p3'] == 30
+    True
+    >>> a.m5(100) == a.m5(100) == 500
+    True
+    >>> a.m7 == a['m7'] == '_m7'
+    True
+    >>> a.g == a['g'] == ('getattr_hook', a, 'g')
+    True
+
+    >>> b = DualOperatorWithSlots(22)
+    >>> b.x == b['x'] == 15
+    True
+    >>> b.z == b['z'] == 22
+    True
+    >>> b.p2 == b['p2'] == 30
+    True
+    >>> b.m5(200) == b['m5'](200) == 1000
+    True
+    >>> b.g == b['g'] == ('getattr_hook', b, 'g')
+    True
+
+
+Interestingly, attribute lookup doesn't call :meth:`object.__getattribute__`
+directly.  Instead, both the dot operator and the :func:`getattr` function
+perform attribute lookup by way of a helper function:
+
+.. testcode::
+
+    def getattr_hook(obj, name):
+        "Emulate slot_tp_getattr_hook() in Objects/typeobject.c"
+        try:
+            return obj.__getattribute__(name)
+        except AttributeError:
+            if not hasattr(type(obj), '__getattr__'):
+                raise
+        return type(obj).__getattr__(obj, name)             # __getattr__
+
+So if :meth:`__getattr__` exists, it is called whenever :meth:`__getattribute__`
+raises :exc:`AttributeError` (either directly or in one of the descriptor calls).
+
+Also, if a user calls :meth:`object.__getattribute__` directly, the
+:meth:`__getattr__` hook is bypassed entirely.
+
+
+Invocation from a class
+-----------------------
+
+The logic for a dotted lookup such as ``A.x`` is in
+:meth:`type.__getattribute__`.  The steps are similar to those for
+:meth:`object.__getattribute__` but the instance dictionary lookup is replaced
+by a search through the class's :term:`method resolution order`.
+
+If a descriptor is found, it is invoked with ``desc.__get__(None, A)``.
+
+The full C implementation can be found in :c:func:`type_getattro()` and
+:c:func:`_PyType_Lookup()` in :source:`Objects/typeobject.c`.
+
+
+Invocation from super
+---------------------
+
+The logic for super's dotted lookup is in the :meth:`__getattribute__` method for
+object returned by :class:`super()`.
+
+A dotted lookup such as ``super(A, obj).m`` searches ``obj.__class__.__mro__``
+for the base class ``B`` immediately following ``A`` and then returns
+``B.__dict__['m'].__get__(obj, A)``.  If not a descriptor, ``m`` is returned
+unchanged.
+
+The full C implementation can be found in :c:func:`super_getattro()` in
+:source:`Objects/typeobject.c`.  A pure Python equivalent can be found in
+`Guido's Tutorial
+<https://www.python.org/download/releases/2.2.3/descrintro/#cooperation>`_.
+
+
+Summary of invocation logic
+---------------------------
+
+The mechanism for descriptors is embedded in the :meth:`__getattribute__()`
+methods for :class:`object`, :class:`type`, and :func:`super`.
 
 The important points to remember are:
 
-* descriptors are invoked by the :meth:`__getattribute__` method
-* overriding :meth:`__getattribute__` prevents automatic descriptor calls
+* Descriptors are invoked by the :meth:`__getattribute__` method.
+
+* Classes inherit this machinery from :class:`object`, :class:`type`, or
+  :func:`super`.
+
+* Overriding :meth:`__getattribute__` prevents automatic descriptor calls
+  because all the descriptor logic is in that method.
+
 * :meth:`object.__getattribute__` and :meth:`type.__getattribute__` make
-  different calls to :meth:`__get__`.
-* data descriptors always override instance dictionaries.
-* non-data descriptors may be overridden by instance dictionaries.
+  different calls to :meth:`__get__`.  The first includes the instance and may
+  include the class.  The second puts in ``None`` for the instance and always
+  includes the class.
 
-The object returned by ``super()`` also has a custom :meth:`__getattribute__`
-method for invoking descriptors.  The attribute lookup ``super(B, obj).m`` searches
-``obj.__class__.__mro__`` for the base class ``A`` immediately following ``B``
-and then returns ``A.__dict__['m'].__get__(obj, B)``.  If not a descriptor,
-``m`` is returned unchanged.  If not in the dictionary, ``m`` reverts to a
-search using :meth:`object.__getattribute__`.
+* Data descriptors always override instance dictionaries.
 
-The implementation details are in :c:func:`super_getattro()` in
-:source:`Objects/typeobject.c`.  and a pure Python equivalent can be found in
-`Guido's Tutorial`_.
+* Non-data descriptors may be overridden by instance dictionaries.
 
-.. _`Guido's Tutorial`: https://www.python.org/download/releases/2.2.3/descrintro/#cooperation
 
-The details above show that the mechanism for descriptors is embedded in the
-:meth:`__getattribute__()` methods for :class:`object`, :class:`type`, and
-:func:`super`.  Classes inherit this machinery when they derive from
-:class:`object` or if they have a meta-class providing similar functionality.
-Likewise, classes can turn-off descriptor invocation by overriding
-:meth:`__getattribute__()`.
+Automatic name notification
+---------------------------
 
+Sometimes it is desirable for a descriptor to know what class variable name it
+was assigned to.  When a new class is created, the :class:`type` metaclass
+scans the dictionary of the new class.  If any of the entries are descriptors
+and if they define :meth:`__set_name__`, that method is called with two
+arguments.  The *owner* is the class where the descriptor is used, and the
+*name* is the class variable the descriptor was assigned to.
 
-Descriptor Example
-------------------
+The implementation details are in :c:func:`type_new()` and
+:c:func:`set_names()` in :source:`Objects/typeobject.c`.
 
-The following code creates a class whose objects are data descriptors which
-print a message for each get or set.  Overriding :meth:`__getattribute__` is
-alternate approach that could do this for every attribute.  However, this
-descriptor is useful for monitoring just a few chosen attributes::
+Since the update logic is in :meth:`type.__new__`, notifications only take
+place at the time of class creation.  If descriptors are added to the class
+afterwards, :meth:`__set_name__` will need to be called manually.
 
-    class RevealAccess:
-        """A data descriptor that sets and returns values
-           normally and prints a message logging their access.
-        """
 
-        def __init__(self, initval=None, name='var'):
-            self.val = initval
-            self.name = name
+ORM example
+-----------
 
-        def __get__(self, obj, objtype):
-            print('Retrieving', self.name)
-            return self.val
+The following code is simplified skeleton showing how data descriptors could
+be used to implement an `object relational mapping
+<https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapping>`_.
 
-        def __set__(self, obj, val):
-            print('Updating', self.name)
-            self.val = val
+The essential idea is that the data is stored in an external database.  The
+Python instances only hold keys to the database's tables.  Descriptors take
+care of lookups or updates:
 
-    >>> class MyClass:
-    ...     x = RevealAccess(10, 'var "x"')
-    ...     y = 5
-    ...
-    >>> m = MyClass()
-    >>> m.x
-    Retrieving var "x"
-    10
-    >>> m.x = 20
-    Updating var "x"
-    >>> m.x
-    Retrieving var "x"
-    20
-    >>> m.y
-    5
+.. testcode::
+
+    class Field:
+
+        def __set_name__(self, owner, name):
+            self.fetch = f'SELECT {name} FROM {owner.table} WHERE {owner.key}=?;'
+            self.store = f'UPDATE {owner.table} SET {name}=? WHERE {owner.key}=?;'
+
+        def __get__(self, obj, objtype=None):
+            return conn.execute(self.fetch, [obj.key]).fetchone()[0]
+
+        def __set__(self, obj, value):
+            conn.execute(self.store, [value, obj.key])
+            conn.commit()
+
+We can use the :class:`Field` class to define `models
+<https://en.wikipedia.org/wiki/Database_model>`_ that describe the schema for
+each table in a database:
+
+.. testcode::
+
+    class Movie:
+        table = 'Movies'                    # Table name
+        key = 'title'                       # Primary key
+        director = Field()
+        year = Field()
+
+        def __init__(self, key):
+            self.key = key
+
+    class Song:
+        table = 'Music'
+        key = 'title'
+        artist = Field()
+        year = Field()
+        genre = Field()
+
+        def __init__(self, key):
+            self.key = key
 
-The protocol is simple and offers exciting possibilities.  Several use cases are
-so common that they have been packaged into individual function calls.
-Properties, bound methods, static methods, and class methods are all
-based on the descriptor protocol.
+To use the models, first connect to the database::
+
+    >>> import sqlite3
+    >>> conn = sqlite3.connect('entertainment.db')
+
+An interactive session shows how data is retrieved from the database and how
+it can be updated:
+
+.. testsetup::
+
+    song_data = [
+        ('Country Roads', 'John Denver', 1972),
+        ('Me and Bobby McGee', 'Janice Joplin', 1971),
+        ('Coal Miners Daughter', 'Loretta Lynn', 1970),
+    ]
+
+    movie_data = [
+        ('Star Wars', 'George Lucas', 1977),
+        ('Jaws', 'Steven Spielberg', 1975),
+        ('Aliens', 'James Cameron', 1986),
+    ]
+
+    import sqlite3
+
+    conn = sqlite3.connect(':memory:')
+    conn.execute('CREATE TABLE Music (title text, artist text, year integer);')
+    conn.execute('CREATE INDEX MusicNdx ON Music (title);')
+    conn.executemany('INSERT INTO Music VALUES (?, ?, ?);', song_data)
+    conn.execute('CREATE TABLE Movies (title text, director text, year integer);')
+    conn.execute('CREATE INDEX MovieNdx ON Music (title);')
+    conn.executemany('INSERT INTO Movies VALUES (?, ?, ?);', movie_data)
+    conn.commit()
+
+.. doctest::
+
+    >>> Movie('Star Wars').director
+    'George Lucas'
+    >>> jaws = Movie('Jaws')
+    >>> f'Released in {jaws.year} by {jaws.director}'
+    'Released in 1975 by Steven Spielberg'
+
+    >>> Song('Country Roads').artist
+    'John Denver'
+
+    >>> Movie('Star Wars').director = 'J.J. Abrams'
+    >>> Movie('Star Wars').director
+    'J.J. Abrams'
+
+
+Pure Python Equivalents
+^^^^^^^^^^^^^^^^^^^^^^^
+
+The descriptor protocol is simple and offers exciting possibilities.  Several
+use cases are so common that they have been prepackaged into built-in tools.
+Properties, bound methods, static methods, class methods, and \_\_slots\_\_ are
+all based on the descriptor protocol.
 
 
 Properties
 ----------
 
 Calling :func:`property` is a succinct way of building a data descriptor that
-triggers function calls upon access to an attribute.  Its signature is::
+triggers a function call upon access to an attribute.  Its signature is::
 
-    property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
+    property(fget=None, fset=None, fdel=None, doc=None) -> property
 
-The documentation shows a typical use to define a managed attribute ``x``::
+The documentation shows a typical use to define a managed attribute ``x``:
+
+.. testcode::
 
     class C:
         def getx(self): return self.__x
@@ -201,7 +920,9 @@ The documentation shows a typical use to define a managed attribute ``x``::
         x = property(getx, setx, delx, "I'm the 'x' property.")
 
 To see how :func:`property` is implemented in terms of the descriptor protocol,
-here is a pure Python equivalent::
+here is a pure Python equivalent:
+
+.. testcode::
 
     class Property:
         "Emulate PyProperty_Type() in Objects/descrobject.c"
@@ -240,6 +961,57 @@ here is a pure Python equivalent::
         def deleter(self, fdel):
             return type(self)(self.fget, self.fset, fdel, self.__doc__)
 
+.. testcode::
+    :hide:
+
+    # Verify the Property() emulation
+
+    class CC:
+        def getx(self):
+            return self.__x
+        def setx(self, value):
+            self.__x = value
+        def delx(self):
+            del self.__x
+        x = Property(getx, setx, delx, "I'm the 'x' property.")
+
+    # Now do it again but use the decorator style
+
+    class CCC:
+        @Property
+        def x(self):
+            return self.__x
+        @x.setter
+        def x(self, value):
+            self.__x = value
+        @x.deleter
+        def x(self):
+            del self.__x
+
+
+.. doctest::
+    :hide:
+
+    >>> cc = CC()
+    >>> hasattr(cc, 'x')
+    False
+    >>> cc.x = 33
+    >>> cc.x
+    33
+    >>> del cc.x
+    >>> hasattr(cc, 'x')
+    False
+
+    >>> ccc = CCC()
+    >>> hasattr(ccc, 'x')
+    False
+    >>> ccc.x = 333
+    >>> ccc.x == 333
+    True
+    >>> del ccc.x
+    >>> hasattr(ccc, 'x')
+    False
+
 The :func:`property` builtin helps whenever a user interface has granted
 attribute access and then subsequent changes require the intervention of a
 method.
@@ -248,100 +1020,137 @@ For instance, a spreadsheet class may grant access to a cell value through
 ``Cell('b10').value``. Subsequent improvements to the program require the cell
 to be recalculated on every access; however, the programmer does not want to
 affect existing client code accessing the attribute directly.  The solution is
-to wrap access to the value attribute in a property data descriptor::
+to wrap access to the value attribute in a property data descriptor:
+
+.. testcode::
 
     class Cell:
-        . . .
-        def getvalue(self):
+        ...
+
+        @property
+        def value(self):
             "Recalculate the cell before returning value"
             self.recalc()
             return self._value
-        value = property(getvalue)
 
+Either the built-in :func:`property` or our :func:`Property` equivalent would
+work in this example.
 
-Functions and Methods
+
+Functions and methods
 ---------------------
 
 Python's object oriented features are built upon a function based environment.
 Using non-data descriptors, the two are merged seamlessly.
 
-Class dictionaries store methods as functions.  In a class definition, methods
-are written using :keyword:`def` or :keyword:`lambda`, the usual tools for
-creating functions.  Methods only differ from regular functions in that the
-first argument is reserved for the object instance.  By Python convention, the
-instance reference is called *self* but may be called *this* or any other
-variable name.
+Functions stored in class dictionaries get turned into methods when invoked.
+Methods only differ from regular functions in that the object instance is
+prepended to the other arguments.  By convention, the instance is called
+*self* but could be called *this* or any other variable name.
+
+Methods can be created manually with :class:`types.MethodType` which is
+roughly equivalent to:
 
-To support method calls, functions include the :meth:`__get__` method for
-binding methods during attribute access.  This means that all functions are
-non-data descriptors which return bound methods when they are invoked from an
-object.  In pure Python, it works like this::
+.. testcode::
+
+    class MethodType:
+        "Emulate Py_MethodType in Objects/classobject.c"
+
+        def __init__(self, func, obj):
+            self.__func__ = func
+            self.__self__ = obj
+
+        def __call__(self, *args, **kwargs):
+            func = self.__func__
+            obj = self.__self__
+            return func(obj, *args, **kwargs)
+
+To support automatic creation of methods, functions include the
+:meth:`__get__` method for binding methods during attribute access.  This
+means that functions are non-data descriptors that return bound methods
+during dotted lookup from an instance.  Here's how it works:
+
+.. testcode::
 
     class Function:
-        . . .
+        ...
+
         def __get__(self, obj, objtype=None):
             "Simulate func_descr_get() in Objects/funcobject.c"
             if obj is None:
                 return self
-            return types.MethodType(self, obj)
+            return MethodType(self, obj)
 
-Running the interpreter shows how the function descriptor works in practice::
+Running the following class in the interpreter shows how the function
+descriptor works in practice:
 
-    >>> class D:
-    ...     def f(self, x):
-    ...         return x
-    ...
-    >>> d = D()
+.. testcode::
+
+    class D:
+        def f(self, x):
+             return x
+
+The function has a :term:`qualified name` attribute to support introspection:
+
+.. doctest::
+
+    >>> D.f.__qualname__
+    'D.f'
+
+Accessing the function through the class dictionary does not invoke
+:meth:`__get__`.  Instead, it just returns the underlying function object::
 
-    # Access through the class dictionary does not invoke __get__.
-    # It just returns the underlying function object.
     >>> D.__dict__['f']
     <function D.f at 0x00C45070>
 
-    # Dotted access from a class calls __get__() which just returns
-    # the underlying function unchanged.
+Dotted access from a class calls :meth:`__get__` which just returns the
+underlying function unchanged::
+
     >>> D.f
     <function D.f at 0x00C45070>
 
-    # The function has a __qualname__ attribute to support introspection
-    >>> D.f.__qualname__
-    'D.f'
+The interesting behavior occurs during dotted access from an instance.  The
+dotted lookup calls :meth:`__get__` which returns a bound method object::
 
-    # Dotted access from an instance calls __get__() which returns the
-    # function wrapped in a bound method object
+    >>> d = D()
     >>> d.f
     <bound method D.f of <__main__.D object at 0x00B18C90>>
 
-    # Internally, the bound method stores the underlying function and
-    # the bound instance.
+Internally, the bound method stores the underlying function and the bound
+instance::
+
     >>> d.f.__func__
-    <function D.f at 0x1012e5ae8>
+    <function D.f at 0x00C45070>
+
     >>> d.f.__self__
     <__main__.D object at 0x1012e1f98>
 
+If you have ever wondered where *self* comes from in regular methods or where
+*cls* comes from in class methods, this is it!
+
 
-Static Methods and Class Methods
---------------------------------
+Static methods
+--------------
 
 Non-data descriptors provide a simple mechanism for variations on the usual
 patterns of binding functions into methods.
 
 To recap, functions have a :meth:`__get__` method so that they can be converted
 to a method when accessed as attributes.  The non-data descriptor transforms an
-``obj.f(*args)`` call into ``f(obj, *args)``.  Calling ``klass.f(*args)``
+``obj.f(*args)`` call into ``f(obj, *args)``.  Calling ``cls.f(*args)``
 becomes ``f(*args)``.
 
 This chart summarizes the binding and its two most useful variants:
 
       +-----------------+----------------------+------------------+
       | Transformation  | Called from an       | Called from a    |
-      |                 | Object               | Class            |
+      |                 | object               | class            |
       +=================+======================+==================+
       | function        | f(obj, \*args)       | f(\*args)        |
       +-----------------+----------------------+------------------+
       | staticmethod    | f(\*args)            | f(\*args)        |
       +-----------------+----------------------+------------------+
-      | classmethod     | f(type(obj), \*args) | f(klass, \*args) |
+      | classmethod     | f(type(obj), \*args) | f(cls, \*args)   |
       +-----------------+----------------------+------------------+
 
 Static methods return the underlying function without changes.  Calling either
@@ -362,21 +1171,27 @@ in statistical work but does not directly depend on a particular dataset.
 It can be called either from an object or the class:  ``s.erf(1.5) --> .9332`` or
 ``Sample.erf(1.5) --> .9332``.
 
-Since staticmethods return the underlying function with no changes, the example
-calls are unexciting::
+Since static methods return the underlying function with no changes, the
+example calls are unexciting:
+
+.. testcode::
+
+    class E:
+        @staticmethod
+        def f(x):
+            print(x)
+
+.. doctest::
 
-    >>> class E:
-    ...     def f(x):
-    ...         print(x)
-    ...     f = staticmethod(f)
-    ...
     >>> E.f(3)
     3
     >>> E().f(3)
     3
 
 Using the non-data descriptor protocol, a pure Python version of
-:func:`staticmethod` would look like this::
+:func:`staticmethod` would look like this:
+
+.. doctest::
 
     class StaticMethod:
         "Emulate PyStaticMethod_Type() in Objects/funcobject.c"
@@ -387,44 +1202,59 @@ Using the non-data descriptor protocol, a pure Python version of
         def __get__(self, obj, objtype=None):
             return self.f
 
+
+Class methods
+-------------
+
 Unlike static methods, class methods prepend the class reference to the
 argument list before calling the function.  This format is the same
-for whether the caller is an object or a class::
+for whether the caller is an object or a class:
 
-    >>> class E:
-    ...     def f(klass, x):
-    ...         return klass.__name__, x
-    ...     f = classmethod(f)
-    ...
-    >>> print(E.f(3))
-    ('E', 3)
-    >>> print(E().f(3))
-    ('E', 3)
+.. testcode::
+
+    class F:
+        @classmethod
+        def f(cls, x):
+            return cls.__name__, x
 
+.. doctest::
 
-This behavior is useful whenever the function only needs to have a class
-reference and does not care about any underlying data.  One use for classmethods
-is to create alternate class constructors.  In Python 2.3, the classmethod
-:func:`dict.fromkeys` creates a new dictionary from a list of keys.  The pure
-Python equivalent is::
+    >>> F.f(3)
+    ('F', 3)
+    >>> F().f(3)
+    ('F', 3)
 
-    class Dict:
-        . . .
-        def fromkeys(klass, iterable, value=None):
+This behavior is useful whenever the method only needs to have a class
+reference and does not rely on data stored in a specific instance.  One use for
+class methods is to create alternate class constructors.  For example, the
+classmethod :func:`dict.fromkeys` creates a new dictionary from a list of
+keys.  The pure Python equivalent is:
+
+.. testcode::
+
+    class Dict(dict):
+        @classmethod
+        def fromkeys(cls, iterable, value=None):
             "Emulate dict_fromkeys() in Objects/dictobject.c"
-            d = klass()
+            d = cls()
             for key in iterable:
                 d[key] = value
             return d
-        fromkeys = classmethod(fromkeys)
 
-Now a new dictionary of unique keys can be constructed like this::
+Now a new dictionary of unique keys can be constructed like this:
+
+.. doctest::
 
-    >>> Dict.fromkeys('abracadabra')
-    {'a': None, 'r': None, 'b': None, 'c': None, 'd': None}
+    >>> d = Dict.fromkeys('abracadabra')
+    >>> type(d) is Dict
+    True
+    >>> d
+    {'a': None, 'b': None, 'r': None, 'c': None, 'd': None}
 
 Using the non-data descriptor protocol, a pure Python version of
-:func:`classmethod` would look like this::
+:func:`classmethod` would look like this:
+
+.. testcode::
 
     class ClassMethod:
         "Emulate PyClassMethod_Type() in Objects/funcobject.c"
@@ -432,10 +1262,301 @@ Using the non-data descriptor protocol, a pure Python version of
         def __init__(self, f):
             self.f = f
 
-        def __get__(self, obj, klass=None):
-            if klass is None:
-                klass = type(obj)
-            def newfunc(*args):
-                return self.f(klass, *args)
-            return newfunc
+        def __get__(self, obj, cls=None):
+            if cls is None:
+                cls = type(obj)
+            if hasattr(obj, '__get__'):
+                return self.f.__get__(cls)
+            return MethodType(self.f, cls)
+
+.. testcode::
+    :hide:
+
+    # Verify the emulation works
+    class T:
+        @ClassMethod
+        def cm(cls, x, y):
+            return (cls, x, y)
+
+.. doctest::
+    :hide:
+
+    >>> T.cm(11, 22)
+    (<class 'T'>, 11, 22)
+
+    # Also call it from an instance
+    >>> t = T()
+    >>> t.cm(11, 22)
+    (<class 'T'>, 11, 22)
+
+The code path for ``hasattr(obj, '__get__')`` was added in Python 3.9 and
+makes it possible for :func:`classmethod` to support chained decorators.
+For example, a classmethod and property could be chained together:
 
+.. testcode::
+
+    class G:
+        @classmethod
+        @property
+        def __doc__(cls):
+            return f'A doc for {cls.__name__!r}'
+
+.. doctest::
+
+    >>> G.__doc__
+    "A doc for 'G'"
+
+
+Member objects and __slots__
+----------------------------
+
+When a class defines ``__slots__``, it replaces instance dictionaries with a
+fixed-length array of slot values.  From a user point of view that has
+several effects:
+
+1. Provides immediate detection of bugs due to misspelled attribute
+assignments.  Only attribute names specified in ``__slots__`` are allowed:
+
+.. testcode::
+
+        class Vehicle:
+            __slots__ = ('id_number', 'make', 'model')
+
+.. doctest::
+
+        >>> auto = Vehicle()
+        >>> auto.id_nubmer = 'VYE483814LQEX'
+        Traceback (most recent call last):
+            ...
+        AttributeError: 'Vehicle' object has no attribute 'id_nubmer'
+
+2. Helps create immutable objects where descriptors manage access to private
+attributes stored in ``__slots__``:
+
+.. testcode::
+
+    class Immutable:
+
+        __slots__ = ('_dept', '_name')          # Replace the instance dictionary
+
+        def __init__(self, dept, name):
+            self._dept = dept                   # Store to private attribute
+            self._name = name                   # Store to private attribute
+
+        @property                               # Read-only descriptor
+        def dept(self):
+            return self._dept
+
+        @property
+        def name(self):                         # Read-only descriptor
+            return self._name
+
+.. doctest::
+
+    >>> mark = Immutable('Botany', 'Mark Watney')
+    >>> mark.dept
+    'Botany'
+    >>> mark.dept = 'Space Pirate'
+    Traceback (most recent call last):
+        ...
+    AttributeError: can't set attribute
+    >>> mark.location = 'Mars'
+    Traceback (most recent call last):
+        ...
+    AttributeError: 'Immutable' object has no attribute 'location'
+
+3. Saves memory.  On a 64-bit Linux build, an instance with two attributes
+takes 48 bytes with ``__slots__`` and 152 bytes without.  This `flyweight
+design pattern <https://en.wikipedia.org/wiki/Flyweight_pattern>`_ likely only
+matters when a large number of instances are going to be created.
+
+4. Blocks tools like :func:`functools.cached_property` which require an
+instance dictionary to function correctly:
+
+.. testcode::
+
+    from functools import cached_property
+
+    class CP:
+        __slots__ = ()                          # Eliminates the instance dict
+
+        @cached_property                        # Requires an instance dict
+        def pi(self):
+            return 4 * sum((-1.0)**n / (2.0*n + 1.0)
+                           for n in reversed(range(100_000)))
+
+.. doctest::
+
+    >>> CP().pi
+    Traceback (most recent call last):
+      ...
+    TypeError: No '__dict__' attribute on 'CP' instance to cache 'pi' property.
+
+It is not possible to create an exact drop-in pure Python version of
+``__slots__`` because it requires direct access to C structures and control
+over object memory allocation.  However, we can build a mostly faithful
+simulation where the actual C structure for slots is emulated by a private
+``_slotvalues`` list.  Reads and writes to that private structure are managed
+by member descriptors:
+
+.. testcode::
+
+    null = object()
+
+    class Member:
+
+        def __init__(self, name, clsname, offset):
+            'Emulate PyMemberDef in Include/structmember.h'
+            # Also see descr_new() in Objects/descrobject.c
+            self.name = name
+            self.clsname = clsname
+            self.offset = offset
+
+        def __get__(self, obj, objtype=None):
+            'Emulate member_get() in Objects/descrobject.c'
+            # Also see PyMember_GetOne() in Python/structmember.c
+            value = obj._slotvalues[self.offset]
+            if value is null:
+                raise AttributeError(self.name)
+            return value
+
+        def __set__(self, obj, value):
+            'Emulate member_set() in Objects/descrobject.c'
+            obj._slotvalues[self.offset] = value
+
+        def __delete__(self, obj):
+            'Emulate member_delete() in Objects/descrobject.c'
+            value = obj._slotvalues[self.offset]
+            if value is null:
+                raise AttributeError(self.name)
+            obj._slotvalues[self.offset] = null
+
+        def __repr__(self):
+            'Emulate member_repr() in Objects/descrobject.c'
+            return f'<Member {self.name!r} of {self.clsname!r}>'
+
+The :meth:`type.__new__` method takes care of adding member objects to class
+variables:
+
+.. testcode::
+
+    class Type(type):
+        'Simulate how the type metaclass adds member objects for slots'
+
+        def __new__(mcls, clsname, bases, mapping):
+            'Emuluate type_new() in Objects/typeobject.c'
+            # type_new() calls PyTypeReady() which calls add_methods()
+            slot_names = mapping.get('slot_names', [])
+            for offset, name in enumerate(slot_names):
+                mapping[name] = Member(name, clsname, offset)
+            return type.__new__(mcls, clsname, bases, mapping)
+
+The :meth:`object.__new__` method takes care of creating instances that have
+slots instead of an instance dictionary.  Here is a rough simulation in pure
+Python:
+
+.. testcode::
+
+    class Object:
+        'Simulate how object.__new__() allocates memory for __slots__'
+
+        def __new__(cls, *args):
+            'Emulate object_new() in Objects/typeobject.c'
+            inst = super().__new__(cls)
+            if hasattr(cls, 'slot_names'):
+                empty_slots = [null] * len(cls.slot_names)
+                object.__setattr__(inst, '_slotvalues', empty_slots)
+            return inst
+
+        def __setattr__(self, name, value):
+            'Emulate _PyObject_GenericSetAttrWithDict() Objects/object.c'
+            cls = type(self)
+            if hasattr(cls, 'slot_names') and name not in cls.slot_names:
+                raise AttributeError(
+                    f'{type(self).__name__!r} object has no attribute {name!r}'
+                )
+            super().__setattr__(name, value)
+
+        def __delattr__(self, name):
+            'Emulate _PyObject_GenericSetAttrWithDict() Objects/object.c'
+            cls = type(self)
+            if hasattr(cls, 'slot_names') and name not in cls.slot_names:
+                raise AttributeError(
+                    f'{type(self).__name__!r} object has no attribute {name!r}'
+                )
+            super().__delattr__(name)
+
+To use the simulation in a real class, just inherit from :class:`Object` and
+set the :term:`metaclass` to :class:`Type`:
+
+.. testcode::
+
+    class H(Object, metaclass=Type):
+        'Instance variables stored in slots'
+
+        slot_names = ['x', 'y']
+
+        def __init__(self, x, y):
+            self.x = x
+            self.y = y
+
+At this point, the metaclass has loaded member objects for *x* and *y*::
+
+    >>> from pprint import pp
+    >>> pp(dict(vars(H)))
+    {'__module__': '__main__',
+     '__doc__': 'Instance variables stored in slots',
+     'slot_names': ['x', 'y'],
+     '__init__': <function H.__init__ at 0x7fb5d302f9d0>,
+     'x': <Member 'x' of 'H'>,
+     'y': <Member 'y' of 'H'>}
+
+.. doctest::
+    :hide:
+
+    # We test this separately because the preceding section is not
+    # doctestable due to the hex memory address for the __init__ function
+    >>> isinstance(vars(H)['x'], Member)
+    True
+    >>> isinstance(vars(H)['y'], Member)
+    True
+
+When instances are created, they have a ``slot_values`` list where the
+attributes are stored:
+
+.. doctest::
+
+    >>> h = H(10, 20)
+    >>> vars(h)
+    {'_slotvalues': [10, 20]}
+    >>> h.x = 55
+    >>> vars(h)
+    {'_slotvalues': [55, 20]}
+
+Misspelled or unassigned attributes will raise an exception:
+
+.. doctest::
+
+    >>> h.xz
+    Traceback (most recent call last):
+        ...
+    AttributeError: 'H' object has no attribute 'xz'
+
+.. doctest::
+   :hide:
+
+    # Examples for deleted attributes are not shown because this section
+    # is already a bit lengthy.  We still test that code here.
+    >>> del h.x
+    >>> hasattr(h, 'x')
+    False
+
+    # Also test the code for uninitialized slots
+    >>> class HU(Object, metaclass=Type):
+    ...     slot_names = ['x', 'y']
+    ...
+    >>> hu = HU()
+    >>> hasattr(hu, 'x')
+    False
+    >>> hasattr(hu, 'y')
+    False
index 7a7a4cf..02cd70f 100644 (file)
@@ -696,7 +696,7 @@ The add_argument() method
    * const_ - A constant value required by some action_ and nargs_ selections.
 
    * default_ - The value produced if the argument is absent from the
-     command line.
+     command line and if it is absent from the namespace object.
 
    * type_ - The type to which the command-line argument should be converted.
 
@@ -1006,6 +1006,14 @@ was not present at the command line::
    >>> parser.parse_args([])
    Namespace(foo=42)
 
+If the target namespace already has an attribute set, the action *default*
+will not over write it::
+
+   >>> parser = argparse.ArgumentParser()
+   >>> parser.add_argument('--foo', default=42)
+   >>> parser.parse_args([], namespace=argparse.Namespace(foo=101))
+   Namespace(foo=101)
+
 If the ``default`` value is a string, the parser parses the value as if it
 were a command-line argument.  In particular, the parser applies any type_
 conversion argument, if provided, before setting the attribute on the
@@ -1133,20 +1141,9 @@ container should match the type_ specified::
 
 Any container can be passed as the *choices* value, so :class:`list` objects,
 :class:`set` objects, and custom containers are all supported.
-This includes :class:`enum.Enum`, which could be used to restrain
-argument's choices; if we reuse previous rock/paper/scissors game example,
-this could be as follows::
-
-   >>> from enum import Enum
-   >>> class GameMove(Enum):
-   ...     ROCK = 'rock'
-   ...     PAPER = 'paper'
-   ...     SCISSORS = 'scissors'
-   ...
-   >>> parser = argparse.ArgumentParser(prog='game.py')
-   >>> parser.add_argument('move', type=GameMove, choices=GameMove)
-   >>> parser.parse_args(['rock'])
-   Namespace(move=<GameMove.ROCK: 'rock'>)
+
+Use of :class:`enum.Enum` is not recommended because it is difficult to
+control its appearance in usage, help, and error messages.
 
 
 required
index 755c60f..95cb017 100644 (file)
@@ -1503,6 +1503,13 @@ Async and await
    fields as :class:`For` and :class:`With`, respectively. Only valid in the
    body of an :class:`AsyncFunctionDef`.
 
+.. note::
+   When a string is parsed by :func:`ast.parse`, operator nodes (subclasses
+   of :class:`ast.operator`, :class:`ast.unaryop`, :class:`ast.cmpop`,
+   :class:`ast.boolop` and :class:`ast.expr_context`) on the returned tree
+   will be singletons. Changes to one will be reflected in all other
+   occurrences of the same value (e.g. :class:`ast.Add`).
+
 
 :mod:`ast` Helpers
 ------------------
@@ -1753,6 +1760,34 @@ and classes for traversing abstract syntax trees:
       Added the *indent* option.
 
 
+.. _ast-compiler-flags:
+
+Compiler Flags
+--------------
+
+The following flags may be passed to :func:`compile` in order to change
+effects on the compilation of a program:
+
+.. data:: PyCF_ALLOW_TOP_LEVEL_AWAIT
+
+   Enables support for top-level ``await``, ``async for``, ``async with``
+   and async comprehensions.
+
+   .. versionadded:: 3.8
+
+.. data:: PyCF_ONLY_AST
+
+   Generates and returns an abstract syntax tree instead of returning a
+   compiled code object.
+
+.. data:: PyCF_TYPE_COMMENTS
+
+   Enables support for :pep:`484` and :pep:`526` style type comments
+   (``# type: <type>``, ``# type: ignore <stuff>``).
+
+   .. versionadded:: 3.8
+
+
 .. _ast-cli:
 
 Command-Line Usage
index d9d3232..5e69525 100644 (file)
@@ -209,7 +209,7 @@ implementation used by the asyncio event loop:
    It works reliably even when the asyncio event loop is run in a non-main OS thread.
 
    There is no noticeable overhead when handling a big number of children (*O(1)* each
-   time a child terminates), but stating a thread per process requires extra memory.
+   time a child terminates), but starting a thread per process requires extra memory.
 
    This watcher is used by default.
 
@@ -219,7 +219,7 @@ implementation used by the asyncio event loop:
 
    This implementation registers a :py:data:`SIGCHLD` signal handler on
    instantiation. That can break third-party code that installs a custom handler for
-   `SIGCHLD`.  signal).
+   :py:data:`SIGCHLD` signal.
 
    The watcher avoids disrupting other code spawning processes
    by polling every process explicitly on a :py:data:`SIGCHLD` signal.
index eb1312a..b033034 100644 (file)
@@ -110,10 +110,8 @@ Creating Subprocesses
 
 .. note::
 
-   The default asyncio event loop implementation on **Windows** does not
-   support subprocesses. Subprocesses are available for Windows if a
-   :class:`ProactorEventLoop` is used.
-   See :ref:`Subprocess Support on Windows <asyncio-windows-subprocess>`
+   Subprocesses are available for Windows if a :class:`ProactorEventLoop` is
+   used. See :ref:`Subprocess Support on Windows <asyncio-windows-subprocess>`
    for details.
 
 .. seealso::
index 99f0125..c638f12 100644 (file)
@@ -504,10 +504,10 @@ Waiting Primitives
                             return_when=ALL_COMPLETED)
 
    Run :ref:`awaitable objects <asyncio-awaitables>` in the *aws*
-   set concurrently and block until the condition specified
+   iterable concurrently and block until the condition specified
    by *return_when*.
 
-   The *aws* set must not be empty.
+   The *aws* iterable must not be empty.
 
    Returns two sets of Tasks/Futures: ``(done, pending)``.
 
@@ -593,9 +593,9 @@ Waiting Primitives
 .. function:: as_completed(aws, \*, loop=None, timeout=None)
 
    Run :ref:`awaitable objects <asyncio-awaitables>` in the *aws*
-   set concurrently.  Return an iterator of coroutines.
+   iterable concurrently.  Return an iterator of coroutines.
    Each coroutine returned can be awaited to get the earliest next
-   result from the set of the remaining awaitables.
+   result from the iterable of the remaining awaitables.
 
    Raises :exc:`asyncio.TimeoutError` if the timeout occurs before
    all Futures are done.
@@ -618,7 +618,7 @@ Running in Threads
    Asynchronously run function *func* in a separate thread.
 
    Any \*args and \*\*kwargs supplied for this function are directly passed
-   to *func*. Also, the current :class:`contextvars.Context` is propogated,
+   to *func*. Also, the current :class:`contextvars.Context` is propagated,
    allowing context variables from the event loop thread to be accessed in the
    separate thread.
 
index 3c68a15..367d56e 100644 (file)
@@ -19,3 +19,29 @@ information on handling these events.
    specific documentation for actual events raised.
 
 .. audit-event-table::
+
+The following events are raised internally and do not correspond to any
+public API of CPython:
+
++--------------------------+-------------------------------------------+
+| Audit event              | Arguments                                 |
++==========================+===========================================+
+| _winapi.CreateFile       | ``file_name``, ``desired_access``,        |
+|                          | ``share_mode``, ``creation_disposition``, |
+|                          | ``flags_and_attributes``                  |
++--------------------------+-------------------------------------------+
+| _winapi.CreateJunction   | ``src_path``, ``dst_path``                |
++--------------------------+-------------------------------------------+
+| _winapi.CreateNamedPipe  | ``name``, ``open_mode``, ``pipe_mode``    |
++--------------------------+-------------------------------------------+
+| _winapi.CreatePipe       |                                           |
++--------------------------+-------------------------------------------+
+| _winapi.CreateProcess    | ``application_name``, ``command_line``,   |
+|                          | ``current_directory``                     |
++--------------------------+-------------------------------------------+
+| _winapi.OpenProcess      | ``process_id``, ``desired_access``        |
++--------------------------+-------------------------------------------+
+| _winapi.TerminateProcess | ``handle``, ``exit_code``                 |
++--------------------------+-------------------------------------------+
+| ctypes.PyObj_FromPtr     | ``obj``                                   |
++--------------------------+-------------------------------------------+
index 85cdc16..637baf4 100644 (file)
@@ -266,7 +266,6 @@ Below are some examples of typical usage of the :mod:`bz2` module.
 Using :func:`compress` and :func:`decompress` to demonstrate round-trip compression:
 
     >>> import bz2
-
     >>> data = b"""\
     ... Donec rhoncus quis sapien sit amet molestie. Fusce scelerisque vel augue
     ... nec ullamcorper. Nam rutrum pretium placerat. Aliquam vel tristique lorem,
@@ -275,11 +274,9 @@ Using :func:`compress` and :func:`decompress` to demonstrate round-trip compress
     ... Aliquam pharetra lacus non risus vehicula rutrum. Maecenas aliquam leo
     ... felis. Pellentesque semper nunc sit amet nibh ullamcorper, ac elementum
     ... dolor luctus. Curabitur lacinia mi ornare consectetur vestibulum."""
-
     >>> c = bz2.compress(data)
     >>> len(data) / len(c)  # Data compression ratio
     1.513595166163142
-
     >>> d = bz2.decompress(c)
     >>> data == d  # Check equality to original object after round-trip
     True
@@ -287,7 +284,6 @@ Using :func:`compress` and :func:`decompress` to demonstrate round-trip compress
 Using :class:`BZ2Compressor` for incremental compression:
 
     >>> import bz2
-
     >>> def gen_data(chunks=10, chunksize=1000):
     ...     """Yield incremental blocks of chunksize bytes."""
     ...     for _ in range(chunks):
@@ -310,7 +306,6 @@ while ordered, repetitive data usually yields a high compression ratio.
 Writing and reading a bzip2-compressed file in binary mode:
 
     >>> import bz2
-
     >>> data = b"""\
     ... Donec rhoncus quis sapien sit amet molestie. Fusce scelerisque vel augue
     ... nec ullamcorper. Nam rutrum pretium placerat. Aliquam vel tristique lorem,
@@ -319,14 +314,11 @@ Writing and reading a bzip2-compressed file in binary mode:
     ... Aliquam pharetra lacus non risus vehicula rutrum. Maecenas aliquam leo
     ... felis. Pellentesque semper nunc sit amet nibh ullamcorper, ac elementum
     ... dolor luctus. Curabitur lacinia mi ornare consectetur vestibulum."""
-
     >>> with bz2.open("myfile.bz2", "wb") as f:
     ...     # Write compressed data to file
     ...     unused = f.write(data)
-
     >>> with bz2.open("myfile.bz2", "rb") as f:
     ...     # Decompress data from file
     ...     content = f.read()
-
     >>> content == data  # Check equality to original object after round-trip
     True
index db0e25b..2345e78 100644 (file)
@@ -291,7 +291,7 @@ Notes on using :class:`Set` and :class:`MutableSet` as a mixin:
    :meth:`_from_iterable` which calls ``cls(iterable)`` to produce a new set.
    If the :class:`Set` mixin is being used in a class with a different
    constructor signature, you will need to override :meth:`_from_iterable`
-   with a classmethod that can construct new instances from
+   with a classmethod or regular method that can construct new instances from
    an iterable argument.
 
 (2)
index 549ac1b..2ffdb49 100644 (file)
@@ -849,6 +849,9 @@ they add the ability to access fields by name instead of position index.
     Named tuple instances do not have per-instance dictionaries, so they are
     lightweight and require no more memory than regular tuples.
 
+    To support pickling, the named tuple class should be assigned to a variable
+    that matches *typename*.
+
     .. versionchanged:: 3.1
        Added support for *rename*.
 
index 675a9ff..61d6c11 100644 (file)
@@ -236,9 +236,9 @@ to a :class:`ProcessPoolExecutor` will result in deadlock.
    An :class:`Executor` subclass that executes calls asynchronously using a pool
    of at most *max_workers* processes.  If *max_workers* is ``None`` or not
    given, it will default to the number of processors on the machine.
-   If *max_workers* is lower or equal to ``0``, then a :exc:`ValueError`
+   If *max_workers* is less than or equal to ``0``, then a :exc:`ValueError`
    will be raised.
-   On Windows, *max_workers* must be equal or lower than ``61``. If it is not
+   On Windows, *max_workers* must be less than or equal to ``61``. If it is not
    then :exc:`ValueError` will be raised. If *max_workers* is ``None``, then
    the default chosen will be at most ``61``, even if more processors are
    available.
@@ -250,7 +250,7 @@ to a :class:`ProcessPoolExecutor` will result in deadlock.
    each worker process; *initargs* is a tuple of arguments passed to the
    initializer.  Should *initializer* raise an exception, all currently
    pending jobs will raise a :exc:`~concurrent.futures.process.BrokenProcessPool`,
-   as well any attempt to submit more jobs to the pool.
+   as well as any attempt to submit more jobs to the pool.
 
    .. versionchanged:: 3.3
       When one of the worker processes terminates abruptly, a
index 2d6c6d0..bf32d3e 100644 (file)
@@ -1326,6 +1326,21 @@ way is to instantiate one of the following classes:
    libraries use the standard C calling convention, and are assumed to return
    :c:type:`int`.
 
+   On Windows creating a :class:`CDLL` instance may fail even if the DLL name
+   exists. When a dependent DLL of the loaded DLL is not found, a
+   :exc:`OSError` error is raised with the message *"[WinError 126] The
+   specified module could not be found".* This error message does not contain
+   the name of the missing DLL because the Windows API does not return this
+   information making this error hard to diagnose. To resolve this error and
+   determine which DLL is not found, you need to find the list of dependent
+   DLLs and determine which one is not found using Windows debugging and
+   tracing tools.
+
+.. seealso::
+
+    `Microsoft DUMPBIN tool <https://docs.microsoft.com/cpp/build/reference/dependents>`_
+    -- A tool to find DLL dependents.
+
 
 .. class:: OleDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=0)
 
@@ -1618,7 +1633,7 @@ They are instances of a private class:
    ``ctypes.seh_exception`` with argument ``code`` will be raised, allowing an
    audit hook to replace the exception with its own.
 
-.. audit-event:: ctypes.call_function func_pointer,arguments ctype-foreign-functions
+.. audit-event:: ctypes.call_function func_pointer,arguments foreign-functions
 
    Some ways to invoke foreign function calls may raise an auditing event
    ``ctypes.call_function`` with arguments ``function pointer`` and ``arguments``.
@@ -2545,4 +2560,3 @@ Arrays and pointers
 
         Returns the object to which to pointer points.  Assigning to this
         attribute changes the pointer to point to the assigned object.
-
index d0307bd..c1e72d1 100644 (file)
@@ -861,7 +861,7 @@ All of the following opcodes use their arguments.
 
 .. opcode:: LIST_TO_TUPLE
 
-    Pops a list from the stack and pushes a tuple containing the same values.
+   Pops a list from the stack and pushes a tuple containing the same values.
 
    .. versionadded:: 3.9
 
@@ -889,7 +889,7 @@ All of the following opcodes use their arguments.
 
 .. opcode:: DICT_MERGE
 
-    Like :opcode:`DICT_UPDATE` but raises an exception for duplicate keys.
+   Like :opcode:`DICT_UPDATE` but raises an exception for duplicate keys.
 
    .. versionadded:: 3.9
 
@@ -907,14 +907,14 @@ All of the following opcodes use their arguments.
 
 .. opcode:: IS_OP (invert)
 
-    Performs ``is`` comparison, or ``is not`` if ``invert`` is 1.
+   Performs ``is`` comparison, or ``is not`` if ``invert`` is 1.
 
    .. versionadded:: 3.9
 
 
 .. opcode:: CONTAINS_OP (invert)
 
-    Performs ``in`` comparison, or ``not in`` if ``invert`` is 1.
+   Performs ``in`` comparison, or ``not in`` if ``invert`` is 1.
 
    .. versionadded:: 3.9
 
@@ -955,8 +955,8 @@ All of the following opcodes use their arguments.
 
 .. opcode:: JUMP_IF_NOT_EXC_MATCH (target)
 
-    Tests whether the second value on the stack is an exception matching TOS,
-    and jumps if it is not. Pops two values from the stack.
+   Tests whether the second value on the stack is an exception matching TOS,
+   and jumps if it is not. Pops two values from the stack.
 
    .. versionadded:: 3.9
 
index 124085e..9c12b6c 100644 (file)
@@ -150,8 +150,8 @@ are always available.  They are listed here in alphabetical order.
    * If it is an *integer*, the array will have that size and will be
      initialized with null bytes.
 
-   * If it is an object conforming to the *buffer* interface, a read-only buffer
-     of the object will be used to initialize the bytes array.
+   * If it is an object conforming to the :ref:`buffer interface <bufferobjects>`,
+     a read-only buffer of the object will be used to initialize the bytes array.
 
    * If it is an *iterable*, it must be an iterable of integers in the range
      ``0 <= x < 256``, which are used as the initial contents of the array.
@@ -245,26 +245,24 @@ are always available.  They are listed here in alphabetical order.
    interactive statement (in the latter case, expression statements that
    evaluate to something other than ``None`` will be printed).
 
-   The optional arguments *flags* and *dont_inherit* control which :ref:`future
-   statements <future>` affect the compilation of *source*.  If neither
-   is present (or both are zero) the code is compiled with those future
-   statements that are in effect in the code that is calling :func:`compile`.  If the
-   *flags* argument is given and *dont_inherit* is not (or is zero) then the
-   future statements specified by the *flags* argument are used in addition to
-   those that would be used anyway. If *dont_inherit* is a non-zero integer then
-   the *flags* argument is it -- the future statements in effect around the call
-   to compile are ignored.
-
-   Future statements are specified by bits which can be bitwise ORed together to
-   specify multiple statements.  The bitfield required to specify a given feature
-   can be found as the :attr:`~__future__._Feature.compiler_flag` attribute on
-   the :class:`~__future__._Feature` instance in the :mod:`__future__` module.
-
-   The optional argument *flags* also controls whether the compiled source is
-   allowed to contain top-level ``await``, ``async for`` and ``async with``.
-   When the bit ``ast.PyCF_ALLOW_TOP_LEVEL_AWAIT`` is set, the return code
-   object has ``CO_COROUTINE`` set in ``co_code``, and can be interactively
-   executed via ``await eval(code_object)``.
+   The optional arguments *flags* and *dont_inherit* control which
+   :ref:`compiler options <ast-compiler-flags>` should be activated
+   and which :ref:`future features <future>` should be allowed. If neither
+   is present (or both are zero) the code is compiled with the same flags that
+   affect the code that is calling :func:`compile`. If the *flags*
+   argument is given and *dont_inherit* is not (or is zero) then the compiler
+   options and the future statements specified by the *flags* argument are used
+   in addition to those that would be used anyway. If *dont_inherit* is a
+   non-zero integer then the *flags* argument is it -- the flags (future
+   features and compiler options) in the surrounding code are ignored.
+
+   Compiler options and future statements are specified by bits which can be
+   bitwise ORed together to specify multiple options. The bitfield required to
+   specify a given future feature can be found as the
+   :attr:`~__future__._Feature.compiler_flag` attribute on the
+   :class:`~__future__._Feature` instance in the :mod:`__future__` module.
+   :ref:`Compiler flags <ast-compiler-flags>` can be found in :mod:`ast`
+   module, with ``PyCF_`` prefix.
 
    The argument *optimize* specifies the optimization level of the compiler; the
    default value of ``-1`` selects the optimization level of the interpreter as
@@ -768,6 +766,8 @@ are always available.  They are listed here in alphabetical order.
 
    .. impl-detail:: This is the address of the object in memory.
 
+   .. audit-event:: builtins.id id id
+
 
 .. function:: input([prompt])
 
index 14aa184..4869b67 100644 (file)
@@ -73,16 +73,31 @@ The :mod:`functools` module defines the following functions:
            def variance(self):
                return statistics.variance(self._data)
 
-   .. versionadded:: 3.8
+   Note, this decorator interferes with the operation of :pep:`412`
+   key-sharing dictionaries.  This means that instance dictionaries
+   can take more space than usual.
 
-   .. note::
+   Also, this decorator requires that the ``__dict__`` attribute on each instance
+   be a mutable mapping. This means it will not work with some types, such as
+   metaclasses (since the ``__dict__`` attributes on type instances are
+   read-only proxies for the class namespace), and those that specify
+   ``__slots__`` without including ``__dict__`` as one of the defined slots
+   (as such classes don't provide a ``__dict__`` attribute at all).
+
+   If a mutable mapping is not available or if space-efficient key sharing
+   is desired, an effect similar to :func:`cached_property` can be achieved
+   by a stacking :func:`property` on top of :func:`cache`::
 
-      This decorator requires that the ``__dict__`` attribute on each instance
-      be a mutable mapping. This means it will not work with some types, such as
-      metaclasses (since the ``__dict__`` attributes on type instances are
-      read-only proxies for the class namespace), and those that specify
-      ``__slots__`` without including ``__dict__`` as one of the defined slots
-      (as such classes don't provide a ``__dict__`` attribute at all).
+       class DataSet:
+           def __init__(self, sequence_of_numbers):
+               self._data = sequence_of_numbers
+
+           @property
+           @cache
+           def stdev(self):
+               return statistics.stdev(self._data)
+
+   .. versionadded:: 3.8
 
 
 .. function:: cmp_to_key(func)
@@ -651,4 +666,4 @@ callable, weak referencable, and can have attributes.  There are some important
 differences.  For instance, the :attr:`~definition.__name__` and :attr:`__doc__` attributes
 are not created automatically.  Also, :class:`partial` objects defined in
 classes behave like static methods and do not transform into bound methods
-during instance attribute look-up.
\ No newline at end of file
+during instance attribute look-up.
index 43096b0..a59a5d3 100644 (file)
@@ -527,30 +527,33 @@ by typing '_' after '.', either before or after the box is opened.
 Calltips
 ^^^^^^^^
 
-A calltip is shown when one types :kbd:`(` after the name of an *accessible*
-function.  A name expression may include dots and subscripts.  A calltip
-remains until it is clicked, the cursor is moved out of the argument area,
-or :kbd:`)` is typed.  When the cursor is in the argument part of a definition,
-the menu or shortcut display a calltip.
-
-A calltip consists of the function signature and the first line of the
-docstring.  For builtins without an accessible signature, the calltip
-consists of all lines up the fifth line or the first blank line.  These
-details may change.
-
-The set of *accessible* functions depends on what modules have been imported
-into the user process, including those imported by Idle itself,
-and what definitions have been run, all since the last restart.
+A calltip is shown automatically when one types :kbd:`(` after the name
+of an *accessible* function.  A function name expression may include
+dots and subscripts.  A calltip remains until it is clicked, the cursor
+is moved out of the argument area, or :kbd:`)` is typed.  Whenever the
+cursor is in the argument part of a definition, select Edit and "Show
+Call Tip" on the menu or enter its shortcut to display a calltip.
+
+The calltip consists of the function's signature and docstring up to
+the latter's first blank line or the fifth non-blank line.  (Some builtin
+functions lack an accessible signature.)  A '/' or '*' in the signature
+indicates that the preceding or following arguments are passed by
+position or name (keyword) only.  Details are subject to change.
+
+In Shell, the accessible functions depends on what modules have been
+imported into the user process, including those imported by Idle itself,
+and which definitions have been run, all since the last restart.
 
 For example, restart the Shell and enter ``itertools.count(``.  A calltip
-appears because Idle imports itertools into the user process for its own use.
-(This could change.)  Enter ``turtle.write(`` and nothing appears.  Idle does
-not import turtle.  The menu or shortcut do nothing either.  Enter
-``import turtle`` and then ``turtle.write(`` will work.
-
-In an editor, import statements have no effect until one runs the file.  One
-might want to run a file after writing the import statements at the top,
-or immediately run an existing file before editing.
+appears because Idle imports itertools into the user process for its own
+use.  (This could change.)  Enter ``turtle.write(`` and nothing appears.
+Idle does not itself import turtle.  The menu entry and shortcut also do
+nothing.  Enter ``import turtle``.  Thereafter, ``turtle.write(``
+will display a calltip.
+
+In an editor, import statements have no effect until one runs the file.
+One might want to run a file after writing import statements, after
+adding function definitions, or after opening an existing file.
 
 .. _code-context:
 
index 02ecfd9..65681ec 100644 (file)
@@ -174,9 +174,9 @@ example of usage.
 
 .. seealso::
 
-   Documents describing the protocol, and sources and binaries  for servers
-   implementing it, can all be found at the University of Washington's *IMAP
-   Information Center* (https://www.washington.edu/imap/).
+   Documents describing the protocol, sources for servers
+   implementing it, by the University of Washington's IMAP Information Center
+   can all be found at (**Source Code**) https://github.com/uw-imap/imap (**Not Maintained**).
 
 
 .. _imap4-objects:
index b51db9e..9027ba5 100644 (file)
@@ -438,8 +438,9 @@ ABC hierarchy::
             package. This attribute is not set on modules.
 
         - :attr:`__package__`
-            The parent package for the module/package. If the module is
-            top-level then it has a value of the empty string. The
+            The fully-qualified name of the package under which the module was
+            loaded as a submodule (or the empty string for top-level modules).
+            For packages, it is the same as :attr:`__name__`.  The
             :func:`importlib.util.module_for_loader` decorator can handle the
             details for :attr:`__package__`.
 
@@ -1344,8 +1345,8 @@ find and load modules.
 
    (``__loader__``)
 
-   The loader to use for loading.  For namespace packages this should be
-   set to ``None``.
+   The :term:`Loader <loader>` that should be used when loading
+   the module.  :term:`Finders <finder>` should always set this.
 
    .. attribute:: origin
 
@@ -1378,8 +1379,9 @@ find and load modules.
 
    (``__package__``)
 
-   (Read-only) Fully-qualified name of the package to which the module
-   belongs as a submodule (or ``None``).
+   (Read-only) The fully-qualified name of the package under which the module
+   should be loaded as a submodule (or the empty string for top-level modules).
+   For packages, it is the same as :attr:`__name__`.
 
    .. attribute:: has_location
 
index 5f5e664..d6d1f1e 100644 (file)
@@ -202,6 +202,32 @@ write code that handles both IP versions correctly.  Address objects are
 .. _iana-ipv4-special-registry: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
 .. _iana-ipv6-special-registry: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
 
+.. method:: IPv4Address.__format__(fmt)
+
+   Returns a string representation of the IP address, controlled by
+   an explicit format string.
+   *fmt* can be one of the following: ``'s'``, the default option,
+   equivalent to :func:`str`, ``'b'`` for a zero-padded binary string,
+   ``'X'`` or ``'x'`` for an uppercase or lowercase hexadecimal
+   representation, or ``'n'``, which is equivalent to ``'b'`` for IPv4
+   addresses and ``'x'`` for IPv6. For binary and hexadecimal
+   representations, the form specifier ``'#'`` and the grouping option
+   ``'_'`` are available. ``__format__`` is used by ``format``, ``str.format``
+   and f-strings.
+
+      >>> format(ipaddress.IPv4Address('192.168.0.1'))
+      '192.168.0.1'
+      >>> '{:#b}'.format(ipaddress.IPv4Address('192.168.0.1'))
+      '0b11000000101010000000000000000001'
+      >>> f'{ipaddress.IPv6Address("2001:db8::1000"):s}'
+      '2001:db8::1000'
+      >>> format(ipaddress.IPv6Address('2001:db8::1000'), '_X')
+      '2001_0DB8_0000_0000_0000_0000_0000_1000'
+      >>> '{:#_n}'.format(ipaddress.IPv6Address('2001:db8::1000'))
+      '0x2001_0db8_0000_0000_0000_0000_0000_1000'
+
+   .. versionadded:: 3.9
+
 
 .. class:: IPv6Address(address)
 
@@ -246,8 +272,8 @@ write code that handles both IP versions correctly.  Address objects are
    groups consisting entirely of zeroes included.
 
 
-   For the following attributes, see the corresponding documentation of the
-   :class:`IPv4Address` class:
+   For the following attributes and methods, see the corresponding
+   documentation of the :class:`IPv4Address` class:
 
    .. attribute:: packed
    .. attribute:: reverse_pointer
@@ -297,6 +323,12 @@ write code that handles both IP versions correctly.  Address objects are
       the embedded ``(server, client)`` IP address pair.  For any other
       address, this property will be ``None``.
 
+.. method:: IPv6Address.__format__(fmt)
+
+   Refer to the corresponding method documentation in
+   :class:`IPv4Address`.
+
+   .. versionadded:: 3.9
 
 Conversion to Strings and Integers
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
index 107bc51..3de66c9 100644 (file)
@@ -755,7 +755,7 @@ which incur interpreter overhead.
        "Count how many times the predicate is true"
        return sum(map(pred, iterable))
 
-   def padnone(iterable):
+   def pad_none(iterable):
        """Returns the sequence elements and then returns None indefinitely.
 
        Useful for emulating the behavior of the built-in map() function.
@@ -809,7 +809,7 @@ which incur interpreter overhead.
                nexts = cycle(islice(nexts, num_active))
 
    def partition(pred, iterable):
-       'Use a predicate to partition entries into false entries and true entries'
+       "Use a predicate to partition entries into false entries and true entries"
        # partition(is_odd, range(10)) --> 0 2 4 6 8   and  1 3 5 7 9
        t1, t2 = tee(iterable)
        return filterfalse(pred, t1), filter(pred, t2)
@@ -881,7 +881,7 @@ which incur interpreter overhead.
    def random_product(*args, repeat=1):
        "Random selection from itertools.product(*args, **kwds)"
        pools = [tuple(pool) for pool in args] * repeat
-       return tuple(random.choice(pool) for pool in pools)
+       return tuple(map(random.choice, pools))
 
    def random_permutation(iterable, r=None):
        "Random selection from itertools.permutations(iterable, r)"
@@ -900,11 +900,11 @@ which incur interpreter overhead.
        "Random selection from itertools.combinations_with_replacement(iterable, r)"
        pool = tuple(iterable)
        n = len(pool)
-       indices = sorted(random.randrange(n) for i in range(r))
+       indices = sorted(random.choices(range(n), k=r))
        return tuple(pool[i] for i in indices)
 
    def nth_combination(iterable, r, index):
-       'Equivalent to list(combinations(iterable, r))[index]'
+       "Equivalent to list(combinations(iterable, r))[index]"
        pool = tuple(iterable)
        n = len(pool)
        if r < 0 or r > n:
index f82a3b2..94d95d1 100644 (file)
@@ -426,17 +426,14 @@ Supported mailbox formats are Maildir, mbox, MH, Babyl, and MMDF.
 
 .. seealso::
 
-   `maildir man page from qmail <http://www.qmail.org/man/man5/maildir.html>`_
-      The original specification of the format.
+   `maildir man page from Courier <http://www.courier-mta.org/maildir.html>`_
+      A specification of the format. Describes a common extension for
+      supporting folders.
 
    `Using maildir format <https://cr.yp.to/proto/maildir.html>`_
       Notes on Maildir by its inventor. Includes an updated name-creation scheme and
       details on "info" semantics.
 
-   `maildir man page from Courier <http://www.courier-mta.org/maildir.html>`_
-      Another specification of the format. Describes a common extension for supporting
-      folders.
-
 
 .. _mailbox-mbox:
 
@@ -485,11 +482,8 @@ Supported mailbox formats are Maildir, mbox, MH, Babyl, and MMDF.
 
 .. seealso::
 
-   `mbox man page from qmail <http://www.qmail.org/man/man5/mbox.html>`_
-      A specification of the format and its variations.
-
    `mbox man page from tin <http://www.tin.org/bin/man.cgi?section=5&topic=mbox>`_
-      Another specification of the format, with details on locking.
+      A specification of the format, with details on locking.
 
    `Configuring Netscape Mail on Unix: Why The Content-Length Format is Bad <https://www.jwz.org/doc/content-length.html>`_
       An argument for using the original mbox format rather than a variation.
index 6ec1fee..b20e557 100644 (file)
@@ -130,7 +130,7 @@ Number-theoretic and representation functions
 
    Return the greatest common divisor of the specified integer arguments.
    If any of the arguments is nonzero, then the returned value is the largest
-   positive integer that is a divisor af all arguments.  If all arguments
+   positive integer that is a divisor of all arguments.  If all arguments
    are zero, then the returned value is ``0``.  ``gcd()`` without arguments
    returns ``0``.
 
index c9992ee..ab84d39 100644 (file)
@@ -98,7 +98,7 @@ to start a process.  These *start methods* are
   *spawn*
     The parent process starts a fresh python interpreter process.  The
     child process will only inherit those resources necessary to run
-    the process objects :meth:`~Process.run` method.  In particular,
+    the process object's :meth:`~Process.run` method.  In particular,
     unnecessary file descriptors and handles from the parent process
     will not be inherited.  Starting a process using this method is
     rather slow compared to using *fork* or *forkserver*.
@@ -2569,9 +2569,9 @@ Address Formats
   filesystem.
 
 * An ``'AF_PIPE'`` address is a string of the form
-   :samp:`r'\\\\.\\pipe\\{PipeName}'`.  To use :func:`Client` to connect to a named
-   pipe on a remote computer called *ServerName* one should use an address of the
-   form :samp:`r'\\\\{ServerName}\\pipe\\{PipeName}'` instead.
+  :samp:`r'\\\\.\\pipe\\{PipeName}'`.  To use :func:`Client` to connect to a named
+  pipe on a remote computer called *ServerName* one should use an address of the
+  form :samp:`r'\\\\{ServerName}\\pipe\\{PipeName}'` instead.
 
 Note that any string beginning with two backslashes is assumed by default to be
 an ``'AF_PIPE'`` address rather than an ``'AF_UNIX'`` address.
index 64bc735..6e287ab 100644 (file)
@@ -3695,8 +3695,8 @@ written in Python, such as a mail server's external command delivery program.
    The positional-only arguments *path*, *args*, and *env* are similar to
    :func:`execve`.
 
-   The *path* parameter is the path to the executable file.The *path* should
-   contain a directory.Use :func:`posix_spawnp` to pass an executable file
+   The *path* parameter is the path to the executable file.  The *path* should
+   contain a directory.  Use :func:`posix_spawnp` to pass an executable file
    without directory.
 
    The *file_actions* argument may be a sequence of tuples describing actions
index 8e8e377..b293adf 100644 (file)
@@ -209,13 +209,6 @@ Windows Platform
    which means the OS version uses debugging code, i.e. code that checks arguments,
    ranges, etc.
 
-   .. note::
-
-      This function works best with Mark Hammond's
-      :mod:`win32all` package installed, but also on Python 2.3 and
-      later (support for this was added in Python 2.6). It obviously
-      only runs on Win32 compatible platforms.
-
 .. function:: win32_edition()
 
    Returns a string representing the current Windows edition.  Possible
index 2f349b3..9bf9212 100644 (file)
@@ -67,7 +67,7 @@ The :mod:`poplib` module provides two classes:
 
    .. audit-event:: poplib.connect self,host,port poplib.POP3_SSL
 
-   .. audit-event:: poplib.putline self,line popplib.POP3_SSL
+   .. audit-event:: poplib.putline self,line poplib.POP3_SSL
 
       All commands will raise an :ref:`auditing event <auditing>`
       ``poplib.putline`` with arguments ``self`` and ``line``,
index 90366f4..8154dfc 100644 (file)
@@ -253,6 +253,8 @@ Functions for sequences
       order so that the sample is reproducible.
 
 
+.. _real-valued-distributions:
+
 Real-valued distributions
 -------------------------
 
@@ -317,6 +319,13 @@ be found in any statistics text.
    deviation.  This is slightly faster than the :func:`normalvariate` function
    defined below.
 
+   Multithreading note:  When two threads call this function
+   simultaneously, it is possible that they will receive the
+   same return value.  This can be avoided in three ways.
+   1) Have each thread use a different instance of the random
+   number generator. 2) Put locks around all calls. 3) Use the
+   slower, but thread-safe :func:`normalvariate` function instead.
+
 
 .. function:: lognormvariate(mu, sigma)
 
@@ -391,8 +400,8 @@ change across Python versions, but two aspects are guaranteed not to change:
 
 .. _random-examples:
 
-Examples and Recipes
---------------------
+Examples
+--------
 
 Basic examples::
 
@@ -536,3 +545,58 @@ Simulation of arrival times and service deliveries for a multiserver queue::
    a tutorial by `Peter Norvig <http://norvig.com/bio.html>`_ covering
    the basics of probability theory, how to write simulations, and
    how to perform data analysis using Python.
+
+
+Recipes
+-------
+
+The default :func:`.random` returns multiples of 2⁻⁵³ in the range
+*0.0 ≤ x < 1.0*.  All such numbers are evenly spaced and are exactly
+representable as Python floats.  However, many other representable
+floats in that interval are not possible selections.  For example,
+``0.05954861408025609`` isn't an integer multiple of 2⁻⁵³.
+
+The following recipe takes a different approach.  All floats in the
+interval are possible selections.  The mantissa comes from a uniform
+distribution of integers in the range *2⁵² ≤ mantissa < 2⁵³*.  The
+exponent comes from a geometric distribution where exponents smaller
+than *-53* occur half as often as the next larger exponent.
+
+::
+
+    from random import Random
+    from math import ldexp
+
+    class FullRandom(Random):
+
+        def random(self):
+            mantissa = 0x10_0000_0000_0000 | self.getrandbits(52)
+            exponent = -53
+            x = 0
+            while not x:
+                x = self.getrandbits(32)
+                exponent += x.bit_length() - 32
+            return ldexp(mantissa, exponent)
+
+All :ref:`real valued distributions <real-valued-distributions>`
+in the class will use the new method::
+
+    >>> fr = FullRandom()
+    >>> fr.random()
+    0.05954861408025609
+    >>> fr.expovariate(0.25)
+    8.87925541791544
+
+The recipe is conceptually equivalent to an algorithm that chooses from
+all the multiples of 2⁻¹⁰⁷⁴ in the range *0.0 ≤ x < 1.0*.  All such
+numbers are evenly spaced, but most have to be rounded down to the
+nearest representable Python float.  (The value 2⁻¹⁰⁷⁴ is the smallest
+positive unnormalized float and is equal to ``math.ulp(0.0)``.)
+
+
+.. seealso::
+
+   `Generating Pseudo-random Floating-Point Values
+   <https://allendowney.com/research/rand/downey07randfloat.pdf>`_ a
+   paper by Allen B. Downey describing ways to generate more
+   fine-grained floats than normally generated by :func:`.random`.
index 1b094ae..3f51227 100644 (file)
@@ -158,9 +158,9 @@ Directory and files operations
 .. function:: copy(src, dst, *, follow_symlinks=True)
 
    Copies the file *src* to the file or directory *dst*.  *src* and *dst*
-   should be strings.  If *dst* specifies a directory, the file will be
-   copied into *dst* using the base filename from *src*.  Returns the
-   path to the newly created file.
+   should be :term:`path-like objects <path-like object>` or strings.  If
+   *dst* specifies a directory, the file will be copied into *dst* using the
+   base filename from *src*.  Returns the path to the newly created file.
 
    If *follow_symlinks* is false, and *src* is a symbolic link,
    *dst* will be created as a symbolic link.  If *follow_symlinks*
@@ -349,7 +349,7 @@ Directory and files operations
    will be created in or as *dst* and *src* will be removed.
 
    If *copy_function* is given, it must be a callable that takes two arguments
-   *src* and *dst*, and will be used to copy *src* to *dest* if
+   *src* and *dst*, and will be used to copy *src* to *dst* if
    :func:`os.rename` cannot be used.  If the source is a directory,
    :func:`copytree` is called, passing it the :func:`copy_function`. The
    default *copy_function* is :func:`copy2`.  Using :func:`~shutil.copy` as the
index 05b285e..e1daeff 100644 (file)
@@ -117,7 +117,7 @@ The variables defined in the :mod:`signal` module are:
 
    Child process stopped or terminated.
 
-   .. availability:: Windows.
+   .. availability:: Unix.
 
 .. data:: SIGCLD
 
index b424e1b..2e3646f 100644 (file)
@@ -231,7 +231,9 @@ Module contents
 
    Return the path of the user-specific site-packages directory,
    :data:`USER_SITE`.  If it is not initialized yet, this function will also set
-   it, respecting :envvar:`PYTHONNOUSERSITE` and :data:`USER_BASE`.
+   it, respecting :data:`USER_BASE`.  To determine if the user-specific
+   site-packages was added to ``sys.path`` :data:`ENABLE_USER_SITE` should be
+   used.
 
    .. versionadded:: 3.2
 
index a88e358..c1a20fe 100644 (file)
@@ -115,7 +115,7 @@ Protocol) and :rfc:`1869` (SMTP Service Extensions).
       If the *timeout* parameter is set to be zero, it will raise a
       :class:`ValueError` to prevent the creation of a non-blocking socket
 
-.. class:: LMTP(host='', port=LMTP_PORT, local_hostname=None,
+.. class:: LMTP(host='', port=LMTP_PORT, local_hostname=None, \
                 source_address=None[, timeout])
 
    The LMTP protocol, which is very similar to ESMTP, is heavily based on the
index 5bcac20..faf8a76 100755 (executable)
@@ -1091,6 +1091,19 @@ The :mod:`socket` module also offers various network-related services:
    .. versionchanged:: 3.8
       Windows support was added.
 
+   .. note::
+
+      On Windows network interfaces have different names in different contexts
+      (all names are examples):
+
+      * UUID: ``{FB605B73-AAC2-49A6-9A2F-25416AEA0573}``
+      * name: ``ethernet_32770``
+      * friendly name: ``vEthernet (nat)``
+      * description: ``Hyper-V Virtual Ethernet Adapter``
+
+      This function returns names of the second form from the list, ``ethernet_32770``
+      in this example case.
+
 
 .. function:: if_nametoindex(if_name)
 
@@ -1105,6 +1118,9 @@ The :mod:`socket` module also offers various network-related services:
    .. versionchanged:: 3.8
       Windows support was added.
 
+   .. seealso::
+      "Interface name" is a name as documented in :func:`if_nameindex`.
+
 
 .. function:: if_indextoname(if_index)
 
@@ -1119,6 +1135,9 @@ The :mod:`socket` module also offers various network-related services:
    .. versionchanged:: 3.8
       Windows support was added.
 
+   .. seealso::
+      "Interface name" is a name as documented in :func:`if_nameindex`.
+
 
 .. _socket-objects:
 
index c3f5c04..a48cfa1 100644 (file)
@@ -4119,6 +4119,12 @@ The constructors for both classes work the same:
    objects.  If *iterable* is not specified, a new empty set is
    returned.
 
+   Sets can be created by several means:
+
+   * Use a comma-separated list of elements within braces: ``{'jack', 'sjoerd'}``
+   * Use a set comprehension: ``{c for c in 'abracadabra' if c not in 'abc'}``
+   * Use the type constructor: ``set()``, ``set('foobar')``, ``set(['a', 'b', 'foo'])``
+
    Instances of :class:`set` and :class:`frozenset` provide the following
    operations:
 
@@ -4311,6 +4317,14 @@ pairs within braces, for example: ``{'jack': 4098, 'sjoerd': 4127}`` or ``{4098:
    Return a new dictionary initialized from an optional positional argument
    and a possibly empty set of keyword arguments.
 
+   Dictionaries can be created by several means:
+
+   * Use a comma-separated list of ``key: value`` pairs within braces:
+     ``{'jack': 4098, 'sjoerd': 4127}`` or ``{4098: 'jack', 4127: 'sjoerd'}``
+   * Use a dict comprehension: ``{}``, ``{x: x ** 2 for x in range(10)}``
+   * Use the type constructor: ``dict()``,
+     ``dict([('foo', 100), ('bar', 200)])``, ``dict(foo=100, bar=200)``
+
    If no positional argument is given, an empty dictionary is created.
    If a positional argument is given and it is a mapping object, a dictionary
    is created with the same key-value pairs as the mapping object.  Otherwise,
@@ -4717,6 +4731,200 @@ Compared to the overhead of setting up the runtime context, the overhead of a
 single class dictionary lookup is negligible.
 
 
+.. _types-genericalias:
+
+Generic Alias Type
+==================
+
+.. index::
+   object: GenericAlias
+   pair: Generic; Alias
+
+``GenericAlias`` objects are created by subscripting a class (usually a
+container), such as ``list[int]``.  They are intended primarily for
+:term:`type annotations <annotation>`.
+
+Usually, the :ref:`subscription <subscriptions>` of container objects calls the
+method :meth:`__getitem__` of the object.  However, the subscription of some
+containers' classes may call the classmethod :meth:`__class_getitem__` of the
+class instead. The classmethod :meth:`__class_getitem__` should return a
+``GenericAlias`` object.
+
+.. note::
+   If the :meth:`__getitem__` of the class' metaclass is present, it will take
+   precedence over the :meth:`__class_getitem__` defined in the class (see
+   :pep:`560` for more details).
+
+The ``GenericAlias`` object acts as a proxy for :term:`generic types
+<generic type>`, implementing *parameterized generics* - a specific instance
+of a generic which provides the types for container elements.
+
+The user-exposed type for the ``GenericAlias`` object can be accessed from
+:class:`types.GenericAlias` and used for :func:`isinstance` checks.  It can
+also be used to create ``GenericAlias`` objects directly.
+
+.. describe:: T[X, Y, ...]
+
+   Creates a ``GenericAlias`` representing a type ``T`` containing elements
+   of types *X*, *Y*, and more depending on the ``T`` used.
+   For example, a function expecting a :class:`list` containing
+   :class:`float` elements::
+
+      def average(values: list[float]) -> float:
+          return sum(values) / len(values)
+
+   Another example for :term:`mapping` objects, using a :class:`dict`, which
+   is a generic type expecting two type parameters representing the key type
+   and the value type.  In this example, the function expects a ``dict`` with
+   keys of type :class:`str` and values of type :class:`int`::
+
+      def send_post_request(url: str, body: dict[str, int]) -> None:
+          ...
+
+The builtin functions :func:`isinstance` and :func:`issubclass` do not accept
+``GenericAlias`` types for their second argument::
+
+   >>> isinstance([1, 2], list[str])
+   Traceback (most recent call last):
+     File "<stdin>", line 1, in <module>
+   TypeError: isinstance() argument 2 cannot be a parameterized generic
+
+The Python runtime does not enforce :term:`type annotations <annotation>`.
+This extends to generic types and their type parameters. When creating
+an object from a ``GenericAlias``, container elements are not checked
+against their type. For example, the following code is discouraged, but will
+run without errors::
+
+   >>> t = list[str]
+   >>> t([1, 2, 3])
+   [1, 2, 3]
+
+Furthermore, parameterized generics erase type parameters during object
+creation::
+
+   >>> t = list[str]
+   >>> type(t)
+   <class 'types.GenericAlias'>
+
+   >>> l = t()
+   >>> type(l)
+   <class 'list'>
+
+Calling :func:`repr` or :func:`str` on a generic shows the parameterized type::
+
+   >>> repr(list[int])
+   'list[int]'
+
+   >>> str(list[int])
+   'list[int]'
+
+The :meth:`__getitem__` method of generics will raise an exception to disallow
+mistakes like ``dict[str][str]``::
+
+   >>> dict[str][str]
+   Traceback (most recent call last):
+     File "<stdin>", line 1, in <module>
+   TypeError: There are no type variables left in dict[str]
+
+However, such expressions are valid when :ref:`type variables <generics>` are
+used.  The index must have as many elements as there are type variable items
+in the ``GenericAlias`` object's :attr:`__args__ <genericalias.__args__>`. ::
+
+   >>> from typing import TypeVar
+   >>> Y = TypeVar('Y')
+   >>> dict[str, Y][int]
+   dict[str, int]
+
+
+Standard Generic Collections
+----------------------------
+
+These standard library collections support parameterized generics.
+
+* :class:`tuple`
+* :class:`list`
+* :class:`dict`
+* :class:`set`
+* :class:`frozenset`
+* :class:`type`
+* :class:`collections.deque`
+* :class:`collections.defaultdict`
+* :class:`collections.OrderedDict`
+* :class:`collections.Counter`
+* :class:`collections.ChainMap`
+* :class:`collections.abc.Awaitable`
+* :class:`collections.abc.Coroutine`
+* :class:`collections.abc.AsyncIterable`
+* :class:`collections.abc.AsyncIterator`
+* :class:`collections.abc.AsyncGenerator`
+* :class:`collections.abc.Iterable`
+* :class:`collections.abc.Iterator`
+* :class:`collections.abc.Generator`
+* :class:`collections.abc.Reversible`
+* :class:`collections.abc.Container`
+* :class:`collections.abc.Collection`
+* :class:`collections.abc.Callable`
+* :class:`collections.abc.Set`
+* :class:`collections.abc.MutableSet`
+* :class:`collections.abc.Mapping`
+* :class:`collections.abc.MutableMapping`
+* :class:`collections.abc.Sequence`
+* :class:`collections.abc.MutableSequence`
+* :class:`collections.abc.ByteString`
+* :class:`collections.abc.MappingView`
+* :class:`collections.abc.KeysView`
+* :class:`collections.abc.ItemsView`
+* :class:`collections.abc.ValuesView`
+* :class:`contextlib.AbstractContextManager`
+* :class:`contextlib.AbstractAsyncContextManager`
+* :ref:`re.Pattern <re-objects>`
+* :ref:`re.Match <match-objects>`
+
+
+Special Attributes of Generic Alias
+-----------------------------------
+
+All parameterized generics implement special read-only attributes.
+
+.. attribute:: genericalias.__origin__
+
+   This attribute points at the non-parameterized generic class::
+
+      >>> list[int].__origin__
+      <class 'list'>
+
+
+.. attribute:: genericalias.__args__
+
+   This attribute is a :class:`tuple` (possibly of length 1) of generic
+   types passed to the original :meth:`__class_getitem__`
+   of the generic container::
+
+      >>> dict[str, list[int]].__args__
+      (<class 'str'>, list[int])
+
+
+.. attribute:: genericalias.__parameters__
+
+   This attribute is a lazily computed tuple (possibly empty) of unique type
+   variables found in ``__args__``::
+
+      >>> from typing import TypeVar
+
+      >>> T = TypeVar('T')
+      >>> list[T].__parameters__
+      (~T,)
+
+
+.. seealso::
+
+   * :pep:`585` -- "Type Hinting Generics In Standard Collections"
+   * :meth:`__class_getitem__` -- Used to implement parameterized generics.
+   * :ref:`generics` -- Generics in the :mod:`typing` module.
+
+.. versionadded:: 3.9
+
+
 .. _typesother:
 
 Other Built-in Types
index 91f43e9..5542e9b 100644 (file)
@@ -384,10 +384,10 @@ following:
 
 The ``'#'`` option causes the "alternate form" to be used for the
 conversion.  The alternate form is defined differently for different
-types.  This option is only valid for integer, float, complex and
-Decimal types. For integers, when binary, octal, or hexadecimal output
+types.  This option is only valid for integer, float and complex
+types. For integers, when binary, octal, or hexadecimal output
 is used, this option adds the prefix respective ``'0b'``, ``'0o'``, or
-``'0x'`` to the output value. For floats, complex and Decimal the
+``'0x'`` to the output value. For float and complex the
 alternate form causes the result of the conversion to always contain a
 decimal-point character, even if no digits follow it. Normally, a
 decimal-point character appears in the result of these conversions
@@ -476,20 +476,36 @@ with the floating point presentation types listed below (except
 ``'n'`` and ``None``). When doing so, :func:`float` is used to convert the
 integer to a floating point number before formatting.
 
-The available presentation types for floating point and decimal values are:
+The available presentation types for :class:`float` and
+:class:`~decimal.Decimal` values are:
 
    +---------+----------------------------------------------------------+
    | Type    | Meaning                                                  |
    +=========+==========================================================+
-   | ``'e'`` | Exponent notation. Prints the number in scientific       |
-   |         | notation using the letter 'e' to indicate the exponent.  |
-   |         | The default precision is ``6``.                          |
+   | ``'e'`` | Scientific notation. For a given precision ``p``,        |
+   |         | formats the number in scientific notation with the       |
+   |         | letter 'e' separating the coefficient from the exponent. |
+   |         | The coefficient has one digit before and ``p`` digits    |
+   |         | after the decimal point, for a total of ``p + 1``        |
+   |         | significant digits. With no precision given, uses a      |
+   |         | precision of ``6`` digits after the decimal point for    |
+   |         | :class:`float`, and shows all coefficient digits         |
+   |         | for :class:`~decimal.Decimal`. If no digits follow the   |
+   |         | decimal point, the decimal point is also removed unless  |
+   |         | the ``#`` option is used.                                |
    +---------+----------------------------------------------------------+
-   | ``'E'`` | Exponent notation. Same as ``'e'`` except it uses an     |
-   |         | upper case 'E' as the separator character.               |
+   | ``'E'`` | Scientific notation. Same as ``'e'`` except it uses      |
+   |         | an upper case 'E' as the separator character.            |
    +---------+----------------------------------------------------------+
-   | ``'f'`` | Fixed-point notation. Displays the number as a           |
-   |         | fixed-point number.  The default precision is ``6``.     |
+   | ``'f'`` | Fixed-point notation. For a given precision ``p``,       |
+   |         | formats the number as a decimal number with exactly      |
+   |         | ``p`` digits following the decimal point. With no        |
+   |         | precision given, uses a precision of ``6`` digits after  |
+   |         | the decimal point for :class:`float`, and uses a         |
+   |         | precision large enough to show all coefficient digits    |
+   |         | for :class:`~decimal.Decimal`. If no digits follow the   |
+   |         | decimal point, the decimal point is also removed unless  |
+   |         | the ``#`` option is used.                                |
    +---------+----------------------------------------------------------+
    | ``'F'`` | Fixed-point notation. Same as ``'f'``, but converts      |
    |         | ``nan`` to  ``NAN`` and ``inf`` to ``INF``.              |
@@ -518,7 +534,10 @@ The available presentation types for floating point and decimal values are:
    |         | the precision.                                           |
    |         |                                                          |
    |         | A precision of ``0`` is treated as equivalent to a       |
-   |         | precision of ``1``.  The default precision is ``6``.     |
+   |         | precision of ``1``. With no precision given, uses a      |
+   |         | precision of ``6`` significant digits for                |
+   |         | :class:`float`, and shows all coefficient digits         |
+   |         | for :class:`~decimal.Decimal`.                           |
    +---------+----------------------------------------------------------+
    | ``'G'`` | General format. Same as ``'g'`` except switches to       |
    |         | ``'E'`` if the number gets too large. The                |
index 880f252..3df529f 100644 (file)
@@ -31,16 +31,22 @@ always available.
    When an auditing event is raised through the :func:`sys.audit` function, each
    hook will be called in the order it was added with the event name and the
    tuple of arguments. Native hooks added by :c:func:`PySys_AddAuditHook` are
-   called first, followed by hooks added in the current interpreter.
+   called first, followed by hooks added in the current interpreter.  Hooks
+   can then log the event, raise an exception to abort the operation,
+   or terminate the process entirely.
 
    .. audit-event:: sys.addaudithook "" sys.addaudithook
 
-      Raise an auditing event ``sys.addaudithook`` with no arguments. If any
+      Calling :func:`sys.addaudithook` will itself raise an auditing event
+      named ``sys.addaudithook`` with no arguments. If any
       existing hooks raise an exception derived from :class:`RuntimeError`, the
       new hook will not be added and the exception suppressed. As a result,
       callers cannot assume that their hook has been added unless they control
       all existing hooks.
 
+   See the :ref:`audit events table <audit-events>` for all events raised by
+   CPython, and :pep:`578` for the original design discussion.
+
    .. versionadded:: 3.8
 
    .. versionchanged:: 3.8.1
@@ -79,14 +85,23 @@ always available.
 
    .. index:: single: auditing
 
-   Raise an auditing event with any active hooks. The event name is a string
-   identifying the event and its associated schema, which is the number and
-   types of arguments. The schema for a given event is considered public and
-   stable API and should not be modified between releases.
-
-   This function will raise the first exception raised by any hook. In general,
-   these errors should not be handled and should terminate the process as
-   quickly as possible.
+   Raise an auditing event and trigger any active auditing hooks.
+   *event* is a string identifying the event, and *args* may contain
+   optional arguments with more information about the event.  The
+   number and types of arguments for a given event are considered a
+   public and stable API and should not be modified between releases.
+
+   For example, one auditing event is named ``os.chdir``. This event has
+   one argument called *path* that will contain the requested new
+   working directory.
+
+   :func:`sys.audit` will call the existing auditing hooks, passing
+   the event name and arguments, and will re-raise the first exception
+   from any hook. In general, if an exception is raised, it should not
+   be handled and the process should be terminated as quickly as
+   possible. This allows hook implementations to decide how to respond
+   to particular events: they can merely log the event or abort the
+   operation by raising an exception.
 
    Hooks are added using the :func:`sys.addaudithook` or
    :c:func:`PySys_AddAuditHook` functions.
index cca466b..7a114fd 100644 (file)
@@ -445,10 +445,11 @@ be finalized; only the internally used file object will be closed. See the
 
 .. method:: TarFile.extractfile(member)
 
-   Extract a member from the archive as a file object. *member* may be a filename
-   or a :class:`TarInfo` object. If *member* is a regular file or a link, an
-   :class:`io.BufferedReader` object is returned. Otherwise, :const:`None` is
-   returned.
+   Extract a member from the archive as a file object. *member* may be
+   a filename or a :class:`TarInfo` object. If *member* is a regular file or
+   a link, an :class:`io.BufferedReader` object is returned. For all other
+   existing members, :const:`None` is returned. If *member* does not appear
+   in the archive, :exc:`KeyError` is raised.
 
    .. versionchanged:: 3.3
       Return an :class:`io.BufferedReader` object.
index f7e6eba..e24f69c 100644 (file)
@@ -1609,6 +1609,8 @@ script execution tests.
 The :mod:`test.support.bytecode_helper` module provides support for testing
 and inspecting bytecode generation.
 
+.. versionadded:: 3.9
+
 The module defines the following class:
 
 .. class:: BytecodeTestCase(unittest.TestCase)
index 79acdf4..0fe3822 100644 (file)
@@ -109,6 +109,11 @@ Standard names are defined for the following types:
    The type of user-defined functions and functions created by
    :keyword:`lambda`  expressions.
 
+   .. audit-event:: function.__new__ code types.FunctionType
+
+   The audit event only occurs for direct instantiation of function objects,
+   and is not raised for normal compilation.
+
 
 .. data:: GeneratorType
 
@@ -138,10 +143,11 @@ Standard names are defined for the following types:
 
    The type for code objects such as returned by :func:`compile`.
 
-   .. audit-event:: code.__new__ code,filename,name,argcount,posonlyargcount,kwonlyargcount,nlocals,stacksize,flags CodeType
+   .. audit-event:: code.__new__ code,filename,name,argcount,posonlyargcount,kwonlyargcount,nlocals,stacksize,flags types.CodeType
 
    Note that the audited arguments may not match the names or positions
-   required by the initializer.
+   required by the initializer.  The audit event only occurs for direct
+   instantiation of code objects, and is not raised for normal compilation.
 
    .. method:: CodeType.replace(**kwargs)
 
@@ -236,6 +242,25 @@ Standard names are defined for the following types:
          Defaults to ``None``. Previously the attribute was optional.
 
 
+.. class:: GenericAlias(t_origin, t_args)
+
+   The type of :ref:`parameterized generics <types-genericalias>` such as
+   ``list[int]``.
+
+   ``t_origin`` should be a non-parameterized generic class, such as ``list``,
+   ``tuple`` or ``dict``.  ``t_args`` should be a :class:`tuple` (possibly of
+   length 1) of types which parameterize ``t_origin``::
+
+      >>> from types import GenericAlias
+
+      >>> list[int] == GenericAlias(list, (int,))
+      True
+      >>> dict[str, int] == GenericAlias(dict, (str, int))
+      True
+
+   .. versionadded:: 3.9
+
+
 .. class:: TracebackType(tb_next, tb_frame, tb_lasti, tb_lineno)
 
    The type of traceback objects such as found in ``sys.exc_info()[2]``.
@@ -359,7 +384,9 @@ Additional Utility Classes and Functions
                return "{}({})".format(type(self).__name__, ", ".join(items))
 
            def __eq__(self, other):
-               return self.__dict__ == other.__dict__
+               if isinstance(self, SimpleNamespace) and isinstance(other, SimpleNamespace):
+                  return self.__dict__ == other.__dict__
+               return NotImplemented
 
    ``SimpleNamespace`` may be useful as a replacement for ``class NS: pass``.
    However, for a structured record type use :func:`~collections.namedtuple`
index 3900e49..af2cafb 100644 (file)
@@ -509,7 +509,8 @@ These can be used as types in annotations using ``[]``, each having a unique syn
    is equivalent to ``Tuple[Any, ...]``, and in turn to :class:`tuple`.
 
    .. deprecated:: 3.9
-      :class:`builtins.tuple <tuple>` now supports ``[]``. See :pep:`585`.
+      :class:`builtins.tuple <tuple>` now supports ``[]``. See :pep:`585` and
+      :ref:`types-genericalias`.
 
 .. data:: Union
 
@@ -583,7 +584,8 @@ These can be used as types in annotations using ``[]``, each having a unique syn
    :class:`collections.abc.Callable`.
 
    .. deprecated:: 3.9
-      :class:`collections.abc.Callable` now supports ``[]``. See :pep:`585`.
+      :class:`collections.abc.Callable` now supports ``[]``. See :pep:`585` and
+      :ref:`types-genericalias`.
 
 .. class:: Type(Generic[CT_co])
 
@@ -620,7 +622,7 @@ These can be used as types in annotations using ``[]``, each having a unique syn
    :ref:`type variables <generics>`, and unions of any of these types.
    For example::
 
-      def new_non_team_user(user_class: Type[Union[BaseUser, ProUser]]): ...
+      def new_non_team_user(user_class: Type[Union[BasicUser, ProUser]]): ...
 
    ``Type[Any]`` is equivalent to ``Type`` which in turn is equivalent
    to ``type``, which is the root of Python's metaclass hierarchy.
@@ -628,7 +630,8 @@ These can be used as types in annotations using ``[]``, each having a unique syn
    .. versionadded:: 3.5.2
 
    .. deprecated:: 3.9
-      :class:`builtins.type <type>` now supports ``[]``. See :pep:`585`.
+      :class:`builtins.type <type>` now supports ``[]``. See :pep:`585` and
+      :ref:`types-genericalias`.
 
 .. data:: Literal
 
@@ -652,6 +655,12 @@ These can be used as types in annotations using ``[]``, each having a unique syn
 
    .. versionadded:: 3.8
 
+   .. versionchanged:: 3.9.1
+      ``Literal`` now de-duplicates parameters.  Equality comparison of
+      ``Literal`` objects are no longer order dependent. ``Literal`` objects
+      will now raise a :exc:`TypeError` exception during equality comparisons
+      if one of their parameters are not :term:`immutable`.
+
 .. data:: ClassVar
 
    Special type construct to mark class variables.
@@ -821,7 +830,7 @@ These are not used in annotations. They are building blocks for creating generic
 
     Type variables exist primarily for the benefit of static type
     checkers.  They serve as the parameters for generic types as well
-    as for generic function definitions.  See class Generic for more
+    as for generic function definitions.  See :class:`Generic` for more
     information on generic types.  Generic functions work as follows::
 
        def repeat(x: T, n: int) -> Sequence[T]:
@@ -1060,7 +1069,8 @@ Corresponding to built-in types
           ...
 
    .. deprecated:: 3.9
-      :class:`builtins.dict <dict>` now supports ``[]``. See :pep:`585`.
+      :class:`builtins.dict <dict>` now supports ``[]``. See :pep:`585` and
+      :ref:`types-genericalias`.
 
 .. class:: List(list, MutableSequence[T])
 
@@ -1080,7 +1090,8 @@ Corresponding to built-in types
           return [item for item in vector if item > 0]
 
    .. deprecated:: 3.9
-      :class:`builtins.list <list>` now supports ``[]``. See :pep:`585`.
+      :class:`builtins.list <list>` now supports ``[]``. See :pep:`585` and
+      :ref:`types-genericalias`.
 
 .. class:: Set(set, MutableSet[T])
 
@@ -1089,14 +1100,16 @@ Corresponding to built-in types
    to use an abstract collection type such as :class:`AbstractSet`.
 
    .. deprecated:: 3.9
-      :class:`builtins.set <set>` now supports ``[]``. See :pep:`585`.
+      :class:`builtins.set <set>` now supports ``[]``. See :pep:`585` and
+      :ref:`types-genericalias`.
 
 .. class:: FrozenSet(frozenset, AbstractSet[T_co])
 
    A generic version of :class:`builtins.frozenset <frozenset>`.
 
    .. deprecated:: 3.9
-      :class:`builtins.frozenset <frozenset>` now supports ``[]``. See :pep:`585`.
+      :class:`builtins.frozenset <frozenset>` now supports ``[]``. See
+      :pep:`585` and :ref:`types-genericalias`.
 
 .. note:: :data:`Tuple` is a special form.
 
@@ -1110,7 +1123,8 @@ Corresponding to types in :mod:`collections`
    .. versionadded:: 3.5.2
 
    .. deprecated:: 3.9
-      :class:`collections.defaultdict` now supports ``[]``. See :pep:`585`.
+      :class:`collections.defaultdict` now supports ``[]``. See :pep:`585` and
+      :ref:`types-genericalias`.
 
 .. class:: OrderedDict(collections.OrderedDict, MutableMapping[KT, VT])
 
@@ -1119,7 +1133,8 @@ Corresponding to types in :mod:`collections`
    .. versionadded:: 3.7.2
 
    .. deprecated:: 3.9
-      :class:`collections.OrderedDict` now supports ``[]``. See :pep:`585`.
+      :class:`collections.OrderedDict` now supports ``[]``. See :pep:`585` and
+      :ref:`types-genericalias`.
 
 .. class:: ChainMap(collections.ChainMap, MutableMapping[KT, VT])
 
@@ -1129,7 +1144,8 @@ Corresponding to types in :mod:`collections`
    .. versionadded:: 3.6.1
 
    .. deprecated:: 3.9
-      :class:`collections.ChainMap` now supports ``[]``. See :pep:`585`.
+      :class:`collections.ChainMap` now supports ``[]``. See :pep:`585` and
+      :ref:`types-genericalias`.
 
 .. class:: Counter(collections.Counter, Dict[T, int])
 
@@ -1139,7 +1155,8 @@ Corresponding to types in :mod:`collections`
    .. versionadded:: 3.6.1
 
    .. deprecated:: 3.9
-      :class:`collections.Counter` now supports ``[]``. See :pep:`585`.
+      :class:`collections.Counter` now supports ``[]``. See :pep:`585` and
+      :ref:`types-genericalias`.
 
 .. class:: Deque(deque, MutableSequence[T])
 
@@ -1149,7 +1166,8 @@ Corresponding to types in :mod:`collections`
    .. versionadded:: 3.6.1
 
    .. deprecated:: 3.9
-      :class:`collections.deque` now supports ``[]``. See :pep:`585`.
+      :class:`collections.deque` now supports ``[]``. See :pep:`585` and
+      :ref:`types-genericalias`.
 
 Other concrete types
 """"""""""""""""""""
@@ -1174,7 +1192,8 @@ Other concrete types
    ``Match[bytes]``. These types are also in the ``typing.re`` namespace.
 
    .. deprecated:: 3.9
-      Classes ``Pattern`` and ``Match`` from :mod:`re` now support ``[]``. See :pep:`585`.
+      Classes ``Pattern`` and ``Match`` from :mod:`re` now support ``[]``.
+      See :pep:`585` and :ref:`types-genericalias`.
 
 .. class:: Text
 
@@ -1201,7 +1220,8 @@ Corresponding to collections in :mod:`collections.abc`
    A generic version of :class:`collections.abc.Set`.
 
    .. deprecated:: 3.9
-      :class:`collections.abc.Set` now supports ``[]``. See :pep:`585`.
+      :class:`collections.abc.Set` now supports ``[]``. See :pep:`585` and
+      :ref:`types-genericalias`.
 
 .. class:: ByteString(Sequence[int])
 
@@ -1214,7 +1234,8 @@ Corresponding to collections in :mod:`collections.abc`
    annotate arguments of any of the types mentioned above.
 
    .. deprecated:: 3.9
-      :class:`collections.abc.ByteString` now supports ``[]``. See :pep:`585`.
+      :class:`collections.abc.ByteString` now supports ``[]``. See :pep:`585`
+      and :ref:`types-genericalias`.
 
 .. class:: Collection(Sized, Iterable[T_co], Container[T_co])
 
@@ -1223,28 +1244,32 @@ Corresponding to collections in :mod:`collections.abc`
    .. versionadded:: 3.6.0
 
    .. deprecated:: 3.9
-      :class:`collections.abc.Collection` now supports ``[]``. See :pep:`585`.
+      :class:`collections.abc.Collection` now supports ``[]``. See :pep:`585`
+      and :ref:`types-genericalias`.
 
 .. class:: Container(Generic[T_co])
 
    A generic version of :class:`collections.abc.Container`.
 
    .. deprecated:: 3.9
-      :class:`collections.abc.Container` now supports ``[]``. See :pep:`585`.
+      :class:`collections.abc.Container` now supports ``[]``. See :pep:`585`
+      and :ref:`types-genericalias`.
 
 .. class:: ItemsView(MappingView, Generic[KT_co, VT_co])
 
    A generic version of :class:`collections.abc.ItemsView`.
 
    .. deprecated:: 3.9
-      :class:`collections.abc.ItemsView` now supports ``[]``. See :pep:`585`.
+      :class:`collections.abc.ItemsView` now supports ``[]``. See :pep:`585`
+      and :ref:`types-genericalias`.
 
 .. class:: KeysView(MappingView[KT_co], AbstractSet[KT_co])
 
    A generic version of :class:`collections.abc.KeysView`.
 
    .. deprecated:: 3.9
-      :class:`collections.abc.KeysView` now supports ``[]``. See :pep:`585`.
+      :class:`collections.abc.KeysView` now supports ``[]``. See :pep:`585`
+      and :ref:`types-genericalias`.
 
 .. class:: Mapping(Sized, Collection[KT], Generic[VT_co])
 
@@ -1255,49 +1280,56 @@ Corresponding to collections in :mod:`collections.abc`
          return word_list[word]
 
    .. deprecated:: 3.9
-      :class:`collections.abc.Mapping` now supports ``[]``. See :pep:`585`.
+      :class:`collections.abc.Mapping` now supports ``[]``. See :pep:`585`
+      and :ref:`types-genericalias`.
 
 .. class:: MappingView(Sized, Iterable[T_co])
 
    A generic version of :class:`collections.abc.MappingView`.
 
    .. deprecated:: 3.9
-      :class:`collections.abc.MappingView` now supports ``[]``. See :pep:`585`.
+      :class:`collections.abc.MappingView` now supports ``[]``. See :pep:`585`
+      and :ref:`types-genericalias`.
 
 .. class:: MutableMapping(Mapping[KT, VT])
 
    A generic version of :class:`collections.abc.MutableMapping`.
 
    .. deprecated:: 3.9
-      :class:`collections.abc.MutableMapping` now supports ``[]``. See :pep:`585`.
+      :class:`collections.abc.MutableMapping` now supports ``[]``. See
+      :pep:`585` and :ref:`types-genericalias`.
 
 .. class:: MutableSequence(Sequence[T])
 
    A generic version of :class:`collections.abc.MutableSequence`.
 
    .. deprecated:: 3.9
-      :class:`collections.abc.MutableSequence` now supports ``[]``. See :pep:`585`.
+      :class:`collections.abc.MutableSequence` now supports ``[]``. See
+      :pep:`585` and :ref:`types-genericalias`.
 
 .. class:: MutableSet(AbstractSet[T])
 
    A generic version of :class:`collections.abc.MutableSet`.
 
    .. deprecated:: 3.9
-      :class:`collections.abc.MutableSet` now supports ``[]``. See :pep:`585`.
+      :class:`collections.abc.MutableSet` now supports ``[]``. See :pep:`585`
+      and :ref:`types-genericalias`.
 
 .. class:: Sequence(Reversible[T_co], Collection[T_co])
 
    A generic version of :class:`collections.abc.Sequence`.
 
    .. deprecated:: 3.9
-      :class:`collections.abc.Sequence` now supports ``[]``. See :pep:`585`.
+      :class:`collections.abc.Sequence` now supports ``[]``. See :pep:`585`
+      and :ref:`types-genericalias`.
 
 .. class:: ValuesView(MappingView[VT_co])
 
    A generic version of :class:`collections.abc.ValuesView`.
 
    .. deprecated:: 3.9
-      :class:`collections.abc.ValuesView` now supports ``[]``. See :pep:`585`.
+      :class:`collections.abc.ValuesView` now supports ``[]``. See :pep:`585`
+      and :ref:`types-genericalias`.
 
 Corresponding to other types in :mod:`collections.abc`
 """"""""""""""""""""""""""""""""""""""""""""""""""""""
@@ -1307,14 +1339,16 @@ Corresponding to other types in :mod:`collections.abc`
    A generic version of :class:`collections.abc.Iterable`.
 
    .. deprecated:: 3.9
-      :class:`collections.abc.Iterable` now supports ``[]``. See :pep:`585`.
+      :class:`collections.abc.Iterable` now supports ``[]``. See :pep:`585`
+      and :ref:`types-genericalias`.
 
 .. class:: Iterator(Iterable[T_co])
 
    A generic version of :class:`collections.abc.Iterator`.
 
    .. deprecated:: 3.9
-      :class:`collections.abc.Iterator` now supports ``[]``. See :pep:`585`.
+      :class:`collections.abc.Iterator` now supports ``[]``. See :pep:`585`
+      and :ref:`types-genericalias`.
 
 .. class:: Generator(Iterator[T_co], Generic[T_co, T_contra, V_co])
 
@@ -1348,7 +1382,8 @@ Corresponding to other types in :mod:`collections.abc`
               start += 1
 
    .. deprecated:: 3.9
-      :class:`collections.abc.Generator` now supports ``[]``. See :pep:`585`.
+      :class:`collections.abc.Generator` now supports ``[]``. See :pep:`585`
+      and :ref:`types-genericalias`.
 
 .. class:: Hashable
 
@@ -1359,7 +1394,8 @@ Corresponding to other types in :mod:`collections.abc`
    A generic version of :class:`collections.abc.Reversible`.
 
    .. deprecated:: 3.9
-      :class:`collections.abc.Reversible` now supports ``[]``. See :pep:`585`.
+      :class:`collections.abc.Reversible` now supports ``[]``. See :pep:`585`
+      and :ref:`types-genericalias`.
 
 .. class:: Sized
 
@@ -1384,7 +1420,8 @@ Asynchronous programming
    .. versionadded:: 3.5.3
 
    .. deprecated:: 3.9
-      :class:`collections.abc.Coroutine` now supports ``[]``. See :pep:`585`.
+      :class:`collections.abc.Coroutine` now supports ``[]``. See :pep:`585`
+      and :ref:`types-genericalias`.
 
 .. class:: AsyncGenerator(AsyncIterator[T_co], Generic[T_co, T_contra])
 
@@ -1420,7 +1457,8 @@ Asynchronous programming
    .. versionadded:: 3.6.1
 
    .. deprecated:: 3.9
-      :class:`collections.abc.AsyncGenerator` now supports ``[]``. See :pep:`585`.
+      :class:`collections.abc.AsyncGenerator` now supports ``[]``. See
+      :pep:`585` and :ref:`types-genericalias`.
 
 .. class:: AsyncIterable(Generic[T_co])
 
@@ -1429,7 +1467,8 @@ Asynchronous programming
    .. versionadded:: 3.5.2
 
    .. deprecated:: 3.9
-      :class:`collections.abc.AsyncIterable` now supports ``[]``. See :pep:`585`.
+      :class:`collections.abc.AsyncIterable` now supports ``[]``. See :pep:`585`
+      and :ref:`types-genericalias`.
 
 .. class:: AsyncIterator(AsyncIterable[T_co])
 
@@ -1438,7 +1477,8 @@ Asynchronous programming
    .. versionadded:: 3.5.2
 
    .. deprecated:: 3.9
-      :class:`collections.abc.AsyncIterator` now supports ``[]``. See :pep:`585`.
+      :class:`collections.abc.AsyncIterator` now supports ``[]``. See :pep:`585`
+      and :ref:`types-genericalias`.
 
 .. class:: Awaitable(Generic[T_co])
 
@@ -1447,7 +1487,8 @@ Asynchronous programming
    .. versionadded:: 3.5.2
 
    .. deprecated:: 3.9
-      :class:`collections.abc.Awaitable` now supports ``[]``. See :pep:`585`.
+      :class:`collections.abc.Awaitable` now supports ``[]``. See :pep:`585`
+      and :ref:`types-genericalias`.
 
 
 Context manager types
@@ -1461,7 +1502,8 @@ Context manager types
    .. versionadded:: 3.6.0
 
    .. deprecated:: 3.9
-      :class:`collections.contextlib.AbstractContextManager` now supports ``[]``. See :pep:`585`.
+      :class:`contextlib.AbstractContextManager` now supports ``[]``. See
+      :pep:`585` and :ref:`types-genericalias`.
 
 .. class:: AsyncContextManager(Generic[T_co])
 
@@ -1471,7 +1513,8 @@ Context manager types
    .. versionadded:: 3.6.2
 
    .. deprecated:: 3.9
-      :class:`collections.contextlib.AbstractAsyncContextManager` now supports ``[]``. See :pep:`585`.
+      :class:`contextlib.AbstractAsyncContextManager` now supports ``[]``. See
+      :pep:`585` and :ref:`types-genericalias`.
 
 Protocols
 ---------
@@ -1650,6 +1693,9 @@ Introspection helpers
    For a typing object of the form ``X[Y, Z, ...]`` these functions return
    ``X`` and ``(Y, Z, ...)``. If ``X`` is a generic alias for a builtin or
    :mod:`collections` class, it gets normalized to the original class.
+   If ``X`` is a :class:`Union` or :class:`Literal` contained in another
+   generic type, the order of ``(Y, Z, ...)`` may be different from the order
+   of the original arguments ``[Y, Z, ...]`` due to type caching.
    For unsupported objects return ``None`` and ``()`` correspondingly.
    Examples::
 
@@ -1691,7 +1737,7 @@ Constant
 
       If ``from __future__ import annotations`` is used in Python 3.7 or later,
       annotations are not evaluated at function definition time.
-      Instead, the are stored as strings in ``__annotations__``,
+      Instead, they are stored as strings in ``__annotations__``,
       This makes it unnecessary to use quotes around the annotation.
       (see :pep:`563`).
 
index 285bb9e..9fc6620 100644 (file)
@@ -593,8 +593,9 @@ The following decorators and exception implement test skipping and expected fail
 
 .. decorator:: expectedFailure
 
-   Mark the test as an expected failure.  If the test fails it will be
-   considered a success.  If the test passes, it will be considered a failure.
+   Mark the test as an expected failure or error.  If the test fails or errors
+   it will be considered a success.  If the test passes, it will be considered
+   a failure.
 
 .. exception:: SkipTest(reason)
 
@@ -896,8 +897,7 @@ Test cases
    .. method:: assertIs(first, second, msg=None)
                assertIsNot(first, second, msg=None)
 
-      Test that *first* and *second* evaluate (or don't evaluate) to the
-      same object.
+      Test that *first* and *second* are (or are not) the same object.
 
       .. versionadded:: 3.1
 
@@ -1088,7 +1088,8 @@ Test cases
 
       If given, *logger* should be a :class:`logging.Logger` object or a
       :class:`str` giving the name of a logger.  The default is the root
-      logger, which will catch all messages.
+      logger, which will catch all messages that were not blocked by a
+      non-propagating descendent logger.
 
       If given, *level* should be either a numeric logging level or
       its string equivalent (for example either ``"ERROR"`` or
@@ -1945,7 +1946,7 @@ Loading and running tests
 
       A list containing 2-tuples of :class:`TestCase` instances and strings
       holding formatted tracebacks.  Each tuple represents an expected failure
-      of the test case.
+      or error of the test case.
 
    .. attribute:: unexpectedSuccesses
 
@@ -2071,8 +2072,8 @@ Loading and running tests
 
    .. method:: addExpectedFailure(test, err)
 
-      Called when the test case *test* fails, but was marked with the
-      :func:`expectedFailure` decorator.
+      Called when the test case *test* fails or errors, but was marked with
+      the :func:`expectedFailure` decorator.
 
       The default implementation appends a tuple ``(test, formatted_err)`` to
       the instance's :attr:`expectedFailures` attribute, where *formatted_err*
index 2c78cd9..bf72c46 100644 (file)
@@ -132,7 +132,7 @@ module documentation.  This section lists the differences between the API and
           ... # Work with dom.
 
 
-.. method:: Node.writexml(writer, indent="", addindent="", newl="",
+.. method:: Node.writexml(writer, indent="", addindent="", newl="", \
                           encoding=None, standalone=None)
 
    Write XML to the writer object.  The writer receives texts but not bytes as input,
@@ -174,7 +174,7 @@ module documentation.  This section lists the differences between the API and
       The :meth:`toxml` method now preserves the attribute order specified
       by the user.
 
-.. method:: Node.toprettyxml(indent="\\t", newl="\\n", encoding=None,
+.. method:: Node.toprettyxml(indent="\\t", newl="\\n", encoding=None, \
                              standalone=None)
 
    Return a pretty-printed version of the document. *indent* specifies the
index 7725e4d..f4bccf6 100644 (file)
@@ -251,12 +251,18 @@ We can remove elements using :meth:`Element.remove`.  Let's say we want to
 remove all countries with a rank higher than 50::
 
    >>> for country in root.findall('country'):
+   ...     # using root.findall() to avoid removal during traversal
    ...     rank = int(country.find('rank').text)
    ...     if rank > 50:
    ...         root.remove(country)
    ...
    >>> tree.write('output.xml')
 
+Note that concurrent modification while iterating can lead to problems,
+just like when iterating and modifying Python lists or dicts.
+Therefore, the example first collects all matching elements with
+``root.findall()``, and only then iterates over the list of matches.
+
 Our XML now looks like this:
 
 .. code-block:: xml
index fc304a1..8906387 100644 (file)
@@ -187,6 +187,24 @@ Ellipsis
    related to mathematical numbers, but subject to the limitations of numerical
    representation in computers.
 
+   The string representations of the numeric classes, computed by
+   :meth:`__repr__` and :meth:`__str__`, have the following
+   properties:
+
+   * They are valid numeric literals which, when passed to their
+     class constructor, produce an object having the value of the
+     original numeric.
+
+   * The representation is in base 10, when possible.
+
+   * Leading zeros, possibly excepting a single zero before a
+     decimal point, are not shown.
+
+   * Trailing zeros, possibly excepting a single zero after a
+     decimal point, are not shown.
+
+   * A sign is shown only when the number is negative.
+
    Python distinguishes between integers, floating point numbers, and complex
    numbers:
 
@@ -1377,12 +1395,14 @@ Basic customization
    context (e.g., in the condition of an ``if`` statement), Python will call
    :func:`bool` on the value to determine if the result is true or false.
 
-   By default, :meth:`__ne__` delegates to :meth:`__eq__` and
-   inverts the result unless it is ``NotImplemented``.  There are no other
-   implied relationships among the comparison operators, for example,
-   the truth of ``(x<y or x==y)`` does not imply ``x<=y``.
-   To automatically generate ordering operations from a single root operation,
-   see :func:`functools.total_ordering`.
+   By default, ``object`` implements :meth:`__eq__` by using ``is``, returning
+   ``NotImplemented`` in the case of a false comparison:
+   ``True if x is y else NotImplemented``. For :meth:`__ne__`, by default it
+   delegates to :meth:`__eq__` and inverts the result unless it is
+   ``NotImplemented``.  There are no other implied relationships among the
+   comparison operators or default implementations; for example, the truth of
+   ``(x<y or x==y)`` does not imply ``x<=y``. To automatically generate ordering
+   operations from a single root operation, see :func:`functools.total_ordering`.
 
    See the paragraph on :meth:`__hash__` for
    some important notes on creating :term:`hashable` objects which support
@@ -1540,6 +1560,12 @@ access (use of, assignment to, or deletion of ``x.name``) for class instances.
       result of implicit invocation via language syntax or built-in functions.
       See :ref:`special-lookup`.
 
+   .. audit-event:: object.__getattr__ obj,name object.__getattribute__
+
+      For certain sensitive attribute accesses, raises an
+      :ref:`auditing event <auditing>` ``object.__getattr__`` with arguments
+      ``obj`` and ``name``.
+
 
 .. method:: object.__setattr__(self, name, value)
 
@@ -1551,12 +1577,24 @@ access (use of, assignment to, or deletion of ``x.name``) for class instances.
    call the base class method with the same name, for example,
    ``object.__setattr__(self, name, value)``.
 
+   .. audit-event:: object.__setattr__ obj,name,value object.__setattr__
+
+      For certain sensitive attribute assignments, raises an
+      :ref:`auditing event <auditing>` ``object.__setattr__`` with arguments
+      ``obj``, ``name``, ``value``.
+
 
 .. method:: object.__delattr__(self, name)
 
    Like :meth:`__setattr__` but for attribute deletion instead of assignment.  This
    should only be implemented if ``del obj.name`` is meaningful for the object.
 
+   .. audit-event:: object.__delattr__ obj,name object.__delattr__
+
+      For certain sensitive attribute deletions, raises an
+      :ref:`auditing event <auditing>` ``object.__delattr__`` with arguments
+      ``obj`` and ``name``.
+
 
 .. method:: object.__dir__(self)
 
@@ -2130,7 +2168,7 @@ Emulating callable objects
    .. index:: pair: call; instance
 
    Called when the instance is "called" as a function; if this method is defined,
-   ``x(arg1, arg2, ...)`` is a shorthand for ``x.__call__(arg1, arg2, ...)``.
+   ``x(arg1, arg2, ...)`` roughly translates to ``type(x).__call__(x, arg1, ...)``.
 
 
 .. _sequence-types:
@@ -2376,10 +2414,11 @@ left undefined.
 
    .. note::
 
-      If the right operand's type is a subclass of the left operand's type and that
-      subclass provides the reflected method for the operation, this method will be
-      called before the left operand's non-reflected method.  This behavior allows
-      subclasses to override their ancestors' operations.
+      If the right operand's type is a subclass of the left operand's type and
+      that subclass provides a different implementation of the reflected method
+      for the operation, this method will be called before the left operand's
+      non-reflected method. This behavior allows subclasses to override their
+      ancestors' operations.
 
 
 .. method:: object.__iadd__(self, other)
@@ -2778,6 +2817,6 @@ An example of an asynchronous context manager class::
    method—that will instead have the opposite effect of explicitly
    *blocking* such fallback.
 
-.. [#] For operands of the same type, it is assumed that if the non-reflected method
-   (such as :meth:`__add__`) fails the operation is not supported, which is why the
-   reflected method is not called.
+.. [#] For operands of the same type, it is assumed that if the non-reflected
+   method -- such as :meth:`__add__` -- fails then the overall operation is not
+   supported, which is why the reflected method is not called.
index b68c298..512aa5a 100644 (file)
@@ -162,6 +162,8 @@ ambiguities and allow common typos to pass uncaught.
 Displays for lists, sets and dictionaries
 -----------------------------------------
 
+.. index:: single: comprehensions
+
 For constructing a list, a set or a dictionary Python provides special syntax
 called "displays", each of them in two flavors:
 
@@ -260,6 +262,7 @@ Set displays
 
 .. index::
    pair: set; display
+   pair: set; comprehensions
    object: set
    single: {} (curly brackets); set expression
    single: , (comma); expression list
@@ -287,6 +290,7 @@ Dictionary displays
 
 .. index::
    pair: dictionary; display
+   pair: dictionary; comprehensions
    key, datum, key/datum pair
    object: dictionary
    single: {} (curly brackets); dictionary expression
@@ -796,8 +800,8 @@ Subscriptions
    object: dictionary
    pair: sequence; item
 
-A subscription selects an item of a sequence (string, tuple or list) or mapping
-(dictionary) object:
+Subscription of a sequence (string, tuple or list) or mapping (dictionary)
+object usually selects an item from the collection:
 
 .. productionlist:: python-grammar
    subscription: `primary` "[" `expression_list` "]"
@@ -833,6 +837,11 @@ this method will need to explicitly add that support.
 A string's items are characters.  A character is not a separate data type but a
 string of exactly one character.
 
+Subscription of certain :term:`classes <class>` or :term:`types <type>`
+creates a :ref:`generic alias <types-genericalias>`.
+In this case, user-defined classes can support subscription by providing a
+:meth:`__class_getitem__` classmethod.
+
 
 .. _slicings:
 
index 4c36e15..c595242 100644 (file)
@@ -680,7 +680,7 @@ Here are the exact rules used:
 Cached bytecode invalidation
 ----------------------------
 
-Before Python loads cached bytecode from ``.pyc`` file, it checks whether the
+Before Python loads cached bytecode from ``.pyc`` file, it checks whether the
 cache is up-to-date with the source ``.py`` file. By default, Python does this
 by storing the source's last-modified timestamp and size in the cache file when
 writing it. At runtime, the import system then validates the cache file by
@@ -857,9 +857,8 @@ module.  ``find_spec()`` returns a fully populated spec for the module.
 This spec will always have "loader" set (with one exception).
 
 To indicate to the import machinery that the spec represents a namespace
-:term:`portion`, the path entry finder sets "loader" on the spec to
-``None`` and "submodule_search_locations" to a list containing the
-portion.
+:term:`portion`, the path entry finder sets "submodule_search_locations" to
+a list containing the portion.
 
 .. versionchanged:: 3.4
    :meth:`~importlib.abc.PathEntryFinder.find_spec` replaced
@@ -875,18 +874,7 @@ portion.
    :meth:`~importlib.abc.PathEntryFinder.find_loader` takes one argument, the
    fully qualified name of the module being imported.  ``find_loader()``
    returns a 2-tuple where the first item is the loader and the second item
-   is a namespace :term:`portion`.  When the first item (i.e. the loader) is
-   ``None``, this means that while the path entry finder does not have a
-   loader for the named module, it knows that the path entry contributes to
-   a namespace portion for the named module.  This will almost always be the
-   case where Python is asked to import a namespace package that has no
-   physical presence on the file system.  When a path entry finder returns
-   ``None`` for the loader, the second item of the 2-tuple return value must
-   be a sequence, although it can be empty.
-
-   If ``find_loader()`` returns a non-``None`` loader value, the portion is
-   ignored and the loader is returned from the path based finder, terminating
-   the search through the path entries.
+   is a namespace :term:`portion`.
 
    For backwards compatibility with other implementations of the import
    protocol, many path entry finders also support the same,
index 93be327..f8ab2e9 100644 (file)
@@ -874,8 +874,8 @@ can appear before a future statement are:
 * blank lines, and
 * other future statements.
 
-The only feature in Python 3.7 that requires using the future statement is
-``annotations``.
+The only feature that requires using the future statement is
+``annotations`` (see :pep:`563`).
 
 All historical features enabled by the future statement are still recognized
 by Python 3.  The list includes ``absolute_import``, ``division``,
index fa8244a..76c9d92 100644 (file)
@@ -79,9 +79,9 @@ class Annotations(dict):
                                               classes=['stableabi']))
             if par['objtype'] != 'function':
                 continue
-            if not par[0].has_key('names') or not par[0]['names']:
+            if not par[0].has_key('ids') or not par[0]['ids']:
                 continue
-            name = par[0]['names'][0]
+            name = par[0]['ids'][0]
             if name.startswith("c."):
                 name = name[2:]
             entry = self.get(name)
index f08f4ab..2fad9ec 100644 (file)
@@ -31,7 +31,12 @@ from sphinx.util import status_iterator, logging
 from sphinx.util.nodes import split_explicit_title
 from sphinx.writers.text import TextWriter, TextTranslator
 from sphinx.writers.latex import LaTeXTranslator
-from sphinx.domains.python import PyModulelevel, PyClassmember
+
+try:
+    from sphinx.domains.python import PyFunction, PyMethod
+except ImportError:
+    from sphinx.domains.python import PyClassmember as PyMethod
+    from sphinx.domains.python import PyModulelevel as PyFunction
 
 # Support for checking for suspicious markup
 
@@ -238,17 +243,18 @@ class PyDecoratorMixin(object):
         return False
 
 
-class PyDecoratorFunction(PyDecoratorMixin, PyModulelevel):
+class PyDecoratorFunction(PyDecoratorMixin, PyFunction):
     def run(self):
         # a decorator function is a function after all
         self.name = 'py:function'
-        return PyModulelevel.run(self)
+        return PyFunction.run(self)
 
 
-class PyDecoratorMethod(PyDecoratorMixin, PyClassmember):
+# TODO: Use sphinx.domains.python.PyDecoratorMethod when possible
+class PyDecoratorMethod(PyDecoratorMixin, PyMethod):
     def run(self):
         self.name = 'py:method'
-        return PyClassmember.run(self)
+        return PyMethod.run(self)
 
 
 class PyCoroutineMixin(object):
@@ -265,31 +271,31 @@ class PyAwaitableMixin(object):
         return ret
 
 
-class PyCoroutineFunction(PyCoroutineMixin, PyModulelevel):
+class PyCoroutineFunction(PyCoroutineMixin, PyFunction):
     def run(self):
         self.name = 'py:function'
-        return PyModulelevel.run(self)
+        return PyFunction.run(self)
 
 
-class PyCoroutineMethod(PyCoroutineMixin, PyClassmember):
+class PyCoroutineMethod(PyCoroutineMixin, PyMethod):
     def run(self):
         self.name = 'py:method'
-        return PyClassmember.run(self)
+        return PyMethod.run(self)
 
 
-class PyAwaitableFunction(PyAwaitableMixin, PyClassmember):
+class PyAwaitableFunction(PyAwaitableMixin, PyFunction):
     def run(self):
         self.name = 'py:function'
-        return PyClassmember.run(self)
+        return PyFunction.run(self)
 
 
-class PyAwaitableMethod(PyAwaitableMixin, PyClassmember):
+class PyAwaitableMethod(PyAwaitableMixin, PyMethod):
     def run(self):
         self.name = 'py:method'
-        return PyClassmember.run(self)
+        return PyMethod.run(self)
 
 
-class PyAbstractMethod(PyClassmember):
+class PyAbstractMethod(PyMethod):
 
     def handle_signature(self, sig, signode):
         ret = super(PyAbstractMethod, self).handle_signature(sig, signode)
@@ -299,7 +305,7 @@ class PyAbstractMethod(PyClassmember):
 
     def run(self):
         self.name = 'py:method'
-        return PyClassmember.run(self)
+        return PyMethod.run(self)
 
 
 # Support for documenting version of removal in deprecations
index c51f178..1a1c7d0 100644 (file)
@@ -15,7 +15,6 @@
     '3.8': '3.8',
     '3.7': '3.7',
     '3.6': '3.6',
-    '3.5': '3.5',
     '2.7': '2.7',
   };
 
index 7be8d0a..c9777c6 100644 (file)
@@ -5,14 +5,13 @@ c-api/sequence,,:i2,o[i1:i2]
 c-api/tuple,,:high,p[low:high]
 c-api/unicode,,:end,str[start:end]
 c-api/unicode,,:start,unicode[start:start+length]
-distutils/examples,267,`,This is the description of the ``foobar`` package.
+distutils/examples,,`,This is the description of the ``foobar`` package.
 distutils/setupscript,,::,
 extending/embedding,,:numargs,"if(!PyArg_ParseTuple(args, "":numargs""))"
 extending/extending,,:myfunction,"PyArg_ParseTuple(args, ""D:myfunction"", &c);"
 extending/extending,,:set,"if (PyArg_ParseTuple(args, ""O:set_callback"", &temp)) {"
 extending/newtypes,,:call,"if (!PyArg_ParseTuple(args, ""sss:call"", &arg1, &arg2, &arg3)) {"
 faq/programming,,:chr,">=4.0) or 1+f(xc,yc,x*x-y*y+xc,2.0*x*y+yc,k-1,f):f(xc,yc,x,y,k,f):chr("
-faq/programming,,::,for x in sequence[::-1]:
 faq/programming,,:reduce,"print((lambda Ru,Ro,Iu,Io,IM,Sx,Sy:reduce(lambda x,y:x+y,map(lambda y,"
 faq/programming,,:reduce,"Sx=Sx,Sy=Sy:reduce(lambda x,y:x+y,map(lambda x,xc=Ru,yc=yc,Ru=Ru,Ro=Ro,"
 faq/windows,,:d48eceb,"Python 3.6.4 (v3.6.4:d48eceb, Dec 19 2017, 06:04:45) [MSC v.1900 32 bit (Intel)] on win32"
@@ -24,6 +23,9 @@ howto/curses,,:blue,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white.
 howto/curses,,:magenta,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white.  The"
 howto/curses,,:cyan,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white.  The"
 howto/curses,,:white,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white.  The"
+howto/descriptor,,:root,"INFO:root"
+howto/descriptor,,:Updating,"root:Updating"
+howto/descriptor,,:Accessing,"root:Accessing"
 howto/instrumentation,,::,python$target:::function-entry
 howto/instrumentation,,:function,python$target:::function-entry
 howto/instrumentation,,::,python$target:::function-return
@@ -147,8 +149,10 @@ library/ipaddress,,:db8,IPv6Address('2001:db8::')
 library/ipaddress,,::,IPv6Address('2001:db8::')
 library/ipaddress,,:db8,>>> ipaddress.IPv6Address('2001:db8::1000')
 library/ipaddress,,::,>>> ipaddress.IPv6Address('2001:db8::1000')
-library/ipaddress,,:db8,IPv6Address('2001:db8::1000')
-library/ipaddress,,::,IPv6Address('2001:db8::1000')
+library/ipaddress,,:db8,'2001:db8::1000'
+library/ipaddress,,::,'2001:db8::1000'
+library/ipaddress,231,:db8,">>> f'{ipaddress.IPv6Address(""2001:db8::1000""):s}'"
+library/ipaddress,231,::,">>> f'{ipaddress.IPv6Address(""2001:db8::1000""):s}'"
 library/ipaddress,,::,IPv6Address('ff02::5678%1')
 library/ipaddress,,::,fe80::1234
 library/ipaddress,,:db8,">>> ipaddress.ip_address(""2001:db8::1"").reverse_pointer"
index 7a40be7..1c1cb54 100644 (file)
@@ -7,7 +7,6 @@
   <li><a href="https://docs.python.org/3.8/">{% trans %}Python 3.8 (stable){% 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 (security-fixes){% 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 (EOL){% endtrans %}</a></li>
   <li><a href="https://www.python.org/doc/versions/">{% trans %}All versions{% endtrans %}</a></li>
 </ul>
index ff4c797..5c6b65f 100644 (file)
@@ -78,7 +78,7 @@ objects:
    Return the number of times *x* appears in the list.
 
 
-.. method:: list.sort(key=None, reverse=False)
+.. method:: list.sort(*, key=None, reverse=False)
    :noindex:
 
    Sort the items of the list in place (the arguments can be used for sort
index 0ce9646..efe44da 100644 (file)
@@ -273,15 +273,15 @@ Exception Chaining
 ==================
 
 The :keyword:`raise` statement allows an optional :keyword:`from` which enables
-chaining exceptions by setting the ``__cause__`` attribute of the raised
-exception. For example::
+chaining exceptions. For example::
 
-    raise RuntimeError from OSError
+    # exc must be exception instance or None.
+    raise RuntimeError from exc
 
 This can be useful when you are transforming exceptions. For example::
 
     >>> def func():
-    ...    raise IOError
+    ...     raise IOError
     ...
     >>> try:
     ...     func()
@@ -297,12 +297,11 @@ This can be useful when you are transforming exceptions. For example::
     <BLANKLINE>
     Traceback (most recent call last):
       File "<stdin>", line 4, in <module>
-    RuntimeError
+    RuntimeError: Failed to open database
 
-The expression following the :keyword:`from` must be either an exception or
-``None``. Exception chaining happens automatically when an exception is raised
-inside an exception handler or :keyword:`finally` section. Exception chaining
-can be disabled by using ``from None`` idiom:
+Exception chaining happens automatically when an exception is raised inside an
+:keyword:`except` or :keyword:`finally` section. Exception chaining can be
+disabled by using ``from None`` idiom:
 
     >>> try:
     ...     open('database.sqlite')
@@ -313,6 +312,8 @@ can be disabled by using ``from None`` idiom:
       File "<stdin>", line 4, in <module>
     RuntimeError
 
+For more information about chaining mechanics, see :ref:`bltin-exceptions`.
+
 
 .. _tut-userexceptions:
 
index 366a532..4e27cff 100644 (file)
@@ -329,11 +329,16 @@ equivalent :keyword:`try`\ -\ :keyword:`finally` blocks::
 
 If you're not using the :keyword:`with` keyword, then you should call
 ``f.close()`` to close the file and immediately free up any system
-resources used by it. If you don't explicitly close a file, Python's
-garbage collector will eventually destroy the object and close the
-open file for you, but the file may stay open for a while.  Another
-risk is that different Python implementations will do this clean-up at
-different times.
+resources used by it.
+
+.. warning::
+   Calling ``f.write()`` without using the :keyword:`!with` keyword or calling
+   ``f.close()`` **might** result in the arguments
+   of ``f.write()`` not being completely written to the disk, even if the
+   program exits successfully.
+
+..
+   See also https://bugs.python.org/issue17852
 
 After a file object is closed, either by a :keyword:`with` statement
 or by calling ``f.close()``, attempts to use the file object will
index f91ab02..95342f3 100644 (file)
@@ -555,7 +555,7 @@ conflict.
    the interactive session.  You can also change the prompts :data:`sys.ps1` and
    :data:`sys.ps2` and the hook :data:`sys.__interactivehook__` in this file.
 
-   .. audit-event:: cpython.run_startup filename PYTHONSTARTUP
+   .. audit-event:: cpython.run_startup filename envvar-PYTHONSTARTUP
 
       Raises an :ref:`auditing event <auditing>` ``cpython.run_startup`` with
       the filename as the argument when called on startup.
index c8f6e8f..5e724cd 100644 (file)
@@ -67,7 +67,7 @@ The command, if run with ``-h``, will show the available options::
     Once an environment has been created, you may wish to activate it, e.g. by
     sourcing an activate script in its bin directory.
 
-.. versionchanged:: 3.8
+.. versionchanged:: 3.9
    Add ``--upgrade-deps`` option to upgrade pip + setuptools to the latest on PyPI
 
 .. versionchanged:: 3.4
index b95a43c..d0c342e 100644 (file)
@@ -23,8 +23,8 @@ available for application-local distributions.
 
 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.
+Python |version| supports Windows 8.1 and newer. If you require Windows 7
+support, please install Python 3.8.
 
 There are a number of different installers available for Windows, each with
 certain benefits and downsides.
@@ -103,9 +103,9 @@ paths longer than this would not resolve and errors would result.
 
 In the latest versions of Windows, this limitation can be expanded to
 approximately 32,000 characters. Your administrator will need to activate the
-"Enable Win32 long paths" group policy, or set the registry value
-``HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem@LongPathsEnabled``
-to ``1``.
+"Enable Win32 long paths" group policy, or set ``LongPathsEnabled`` to ``1``
+in the registry key
+``HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem``.
 
 This allows the :func:`open` function, the :mod:`os` module and most other
 path functionality to accept and return paths longer than 260 characters.
index a2fa178..0b4820f 100644 (file)
@@ -870,8 +870,18 @@ clipboard.  Converting strings from Tcl to Python and back now never fails.
 (Many people worked on this for eight years but the problem was finally
 solved by Serhiy Storchaka in :issue:`13153`.)
 
+New in 3.8.1:
+
+Add option to toggle cursor blink off.  (Contributed by Zackery Spytz
+in :issue:`4603`.)
+
+Escape key now closes IDLE completion windows.  (Contributed by Johnny
+Najera in :issue:`38944`.)
+
 The changes above have been backported to 3.7 maintenance releases.
 
+Add keywords to module name completion list.  (Contributed by Terry J.
+Reedy in :issue:`37765`.)
 
 inspect
 -------
@@ -2103,9 +2113,6 @@ Changes in the C API
 
   (Contributed by Antoine Pitrou in :issue:`32388`.)
 
-* The :c:func:`PyCode_New` has a new parameter in the second position (*posonlyargcount*)
-  to support :pep:`570`, indicating the number of positional-only arguments.
-
 * The functions :c:func:`PyNode_AddChild` and :c:func:`PyParser_AddToken` now accept
   two additional ``int`` arguments *end_lineno* and *end_col_offset*.
 
index 9a09e71..f8f421b 100644 (file)
@@ -135,8 +135,8 @@ More generally, try to run your tests in the :ref:`Python Development Mode
 <devmode>` which helps to prepare your code to make it compatible with the
 next Python version.
 
-Note: a number of pre-existing deprecatations were removed in this version
-of Python as well. Consult the :ref:`removed-in-python-39` section.
+Note: a number of pre-existing deprecations were removed in this version of
+Python as well. Consult the :ref:`removed-in-python-39` section.
 
 
 New Features
@@ -781,41 +781,41 @@ Here's a summary of performance improvements from Python 3.4 through Python 3.9:
     --------------                       ---     ---     ---     ---     ---    ---
 
     Variable and attribute read access:
-        read_local                       7.1     7.1     5.4     5.1     3.9    4.0
-        read_nonlocal                    7.1     8.1     5.8     5.4     4.4    4.8
-        read_global                     15.5    19.0    14.3    13.6     7.6    7.7
-        read_builtin                    21.1    21.6    18.5    19.0     7.5    7.7
-        read_classvar_from_class        25.6    26.5    20.7    19.5    18.4   18.6
-        read_classvar_from_instance     22.8    23.5    18.8    17.1    16.4   20.1
-        read_instancevar                32.4    33.1    28.0    26.3    25.4   27.7
-        read_instancevar_slots          27.8    31.3    20.8    20.8    20.2   24.5
-        read_namedtuple                 73.8    57.5    45.0    46.8    18.4   23.2
-        read_boundmethod                37.6    37.9    29.6    26.9    27.7   45.9
+        read_local                       7.1     7.1     5.4     5.1     3.9    3.9
+        read_nonlocal                    7.1     8.1     5.8     5.4     4.4    4.5
+        read_global                     15.5    19.0    14.3    13.6     7.6    7.8
+        read_builtin                    21.1    21.6    18.5    19.0     7.5    7.8
+        read_classvar_from_class        25.6    26.5    20.7    19.5    18.4   17.9
+        read_classvar_from_instance     22.8    23.5    18.8    17.1    16.4   16.9
+        read_instancevar                32.4    33.1    28.0    26.3    25.4   25.3
+        read_instancevar_slots          27.8    31.3    20.8    20.8    20.2   20.5
+        read_namedtuple                 73.8    57.5    45.0    46.8    18.4   18.7
+        read_boundmethod                37.6    37.9    29.6    26.9    27.7   41.1
 
     Variable and attribute write access:
-        write_local                      8.7     9.3     5.5     5.3     4.3    4.2
-        write_nonlocal                  10.5    11.1     5.6     5.5     4.7    4.9
-        write_global                    19.7    21.2    18.0    18.0    15.8   17.2
-        write_classvar                  92.9    96.0   104.6   102.1    39.2   43.2
-        write_instancevar               44.6    45.8    40.0    38.9    35.5   40.7
-        write_instancevar_slots         35.6    36.1    27.3    26.6    25.7   27.7
+        write_local                      8.7     9.3     5.5     5.3     4.3    4.3
+        write_nonlocal                  10.5    11.1     5.6     5.5     4.7    4.8
+        write_global                    19.7    21.2    18.0    18.0    15.8   16.7
+        write_classvar                  92.9    96.0   104.6   102.1    39.2   39.8
+        write_instancevar               44.6    45.8    40.0    38.9    35.5   37.4
+        write_instancevar_slots         35.6    36.1    27.3    26.6    25.7   25.8
 
     Data structure read access:
-        read_list                       24.2    24.5    20.8    20.8    19.0   21.1
-        read_deque                      24.7    25.5    20.2    20.6    19.8   21.6
-        read_dict                       24.3    25.7    22.3    23.0    21.0   22.5
-        read_strdict                    22.6    24.3    19.5    21.2    18.9   21.6
+        read_list                       24.2    24.5    20.8    20.8    19.0   19.5
+        read_deque                      24.7    25.5    20.2    20.6    19.8   20.2
+        read_dict                       24.3    25.7    22.3    23.0    21.0   22.4
+        read_strdict                    22.6    24.3    19.5    21.2    18.9   21.5
 
     Data structure write access:
-        write_list                      27.1    28.5    22.5    21.6    20.0   21.6
-        write_deque                     28.7    30.1    22.7    21.8    23.5   23.2
-        write_dict                      31.4    33.3    29.3    29.2    24.7   27.8
-        write_strdict                   28.4    29.9    27.5    25.2    23.1   29.8
+        write_list                      27.1    28.5    22.5    21.6    20.0   20.0
+        write_deque                     28.7    30.1    22.7    21.8    23.5   21.7
+        write_dict                      31.4    33.3    29.3    29.2    24.7   25.4
+        write_strdict                   28.4    29.9    27.5    25.2    23.1   24.5
 
     Stack (or queue) operations:
-        list_append_pop                 93.4   112.7    75.4    74.2    50.8   53.9
-        deque_append_pop                43.5    57.0    49.4    49.2    42.5   45.5
-        deque_append_popleft            43.7    57.3    49.7    49.7    42.8   45.5
+        list_append_pop                 93.4   112.7    75.4    74.2    50.8   50.6
+        deque_append_pop                43.5    57.0    49.4    49.2    42.5   44.2
+        deque_append_popleft            43.7    57.3    49.7    49.7    42.8   46.4
 
     Timing loop:
         loop_overhead                    0.5     0.6     0.4     0.3     0.3    0.3
@@ -891,7 +891,7 @@ Deprecated
   and will be removed in future Python versions.  ``value`` itself should be
   used instead of ``Index(value)``.  ``Tuple(slices, Load())`` should be
   used instead of ``ExtSlice(slices)``.
-  (Contributed by Serhiy Storchaka in :issue:`32892`.)
+  (Contributed by Serhiy Storchaka in :issue:`34822`.)
 
 * :mod:`ast` classes ``Suite``, ``Param``, ``AugLoad`` and ``AugStore``
   are considered deprecated and will be removed in future Python versions.
@@ -1177,7 +1177,7 @@ CPython bytecode changes
 
   * ``COMPARE_OP`` for rich comparisons
   * ``IS_OP`` for 'is' and 'is not' tests
-  * ``CONTAINS_OP`` for 'in' and 'is not' tests
+  * ``CONTAINS_OP`` for 'in' and 'not in' tests
   * ``JUMP_IF_NOT_EXC_MATCH`` for checking exceptions in 'try-except'
     statements.
 
@@ -1289,6 +1289,10 @@ New Features
   representation of a function-like object.
   (Patch by Jeroen Demeyer in :issue:`37645`.)
 
+* Added :c:func:`PyObject_CallOneArg` for calling an object with one
+  positional argument
+  (Patch by Jeroen Demeyer in :issue:`37483`.)
+
 
 Porting to Python 3.9
 ---------------------
@@ -1450,3 +1454,46 @@ Removed
   ``PyNullImporter_Type``, ``PyCmpWrapper_Type``, ``PySortWrapper_Type``,
   ``PyNoArgsFunction``.
   (Contributed by Pablo Galindo Salgado in :issue:`39372`.)
+
+Notable changes in Python 3.9.1
+===============================
+
+typing
+------
+
+The behavior of :class:`typing.Literal` was changed to conform with :pep:`586`
+and to match the behavior of static type checkers specified in the PEP.
+
+1. ``Literal`` now de-duplicates parameters.
+2. Equality comparisons between ``Literal`` objects are now order independent.
+3. ``Literal`` comparisons now respect types.  For example,
+   ``Literal[0] == Literal[False]`` previously evaluated to ``True``.  It is
+   now ``False``.  To support this change, the internally used type cache now
+   supports differentiating types.
+4. ``Literal`` objects will now raise a :exc:`TypeError` exception during
+   equality comparisons if one of their parameters are not :term:`immutable`.
+   Note that declaring ``Literal`` with mutable parameters will not throw
+   an error::
+
+      >>> from typing import Literal
+      >>> Literal[{0}]
+      >>> Literal[{0}] == Literal[{False}]
+      Traceback (most recent call last):
+        File "<stdin>", line 1, in <module>
+      TypeError: unhashable type: 'set'
+
+(Contributed by Yurii Karabas in :issue:`42345`.)
+
+macOS 11.0 (Big Sur) and Apple Silicon Mac support
+--------------------------------------------------
+
+As of 3.9.1, Python now fully supports building and running on macOS 11.0
+(Big Sur) and on Apple Silicon Macs (based on the ``ARM64`` architecture).
+A new universal build variant, ``universal2``, is now available to natively
+support both ``ARM64`` and ``Intel 64`` in one set of executables. Binaries
+can also now be built on current versions of macOS to be deployed on a range
+of older macOS versions (tested to 10.9) while making some newer OS
+functions and options conditionally available based on the operating system
+version in use at runtime ("weaklinking").
+
+(Contributed by Ronald Oussoren and Lawrence D'Anna in :issue:`41100`.)
index 334a7f5..ce78397 100644 (file)
@@ -182,7 +182,7 @@ with_stmt[stmt_ty]:
     | ASYNC 'with' a=','.with_item+ ':' tc=[TYPE_COMMENT] b=block {
        CHECK_VERSION(5, "Async with statements are", _Py_AsyncWith(a, b, NEW_TYPE_COMMENT(p, tc), EXTRA)) }
 with_item[withitem_ty]:
-    | e=expression 'as' t=target &(',' | ')' | ':') { _Py_withitem(e, t, p->arena) }
+    | e=expression 'as' t=star_target &(',' | ')' | ':') { _Py_withitem(e, t, p->arena) }
     | invalid_with_item
     | e=expression { _Py_withitem(e, NULL, p->arena) }
 
@@ -301,7 +301,6 @@ block[asdl_seq*] (memo):
     | simple_stmt
     | invalid_block
 
-expressions_list[asdl_seq*]: a=','.star_expression+ [','] { a }
 star_expressions[expr_ty]:
     | a=star_expression b=(',' c=star_expression { c })+ [','] {
         _Py_Tuple(CHECK(_PyPegen_seq_insert_in_front(p, a, b)), Load, EXTRA) }
@@ -413,7 +412,7 @@ compare_op_bitwise_or_pair[CmpopExprPair*]:
     | is_bitwise_or
 eq_bitwise_or[CmpopExprPair*]: '==' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, Eq, a) }
 noteq_bitwise_or[CmpopExprPair*]:
-    | (tok='!=' {_PyPegen_check_barry_as_flufl(p) ? NULL : tok}) a=bitwise_or {_PyPegen_cmpop_expr_pair(p, NotEq, a) }
+    | (tok='!=' { _PyPegen_check_barry_as_flufl(p, tok) ? NULL : tok}) a=bitwise_or {_PyPegen_cmpop_expr_pair(p, NotEq, a) }
 lte_bitwise_or[CmpopExprPair*]: '<=' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, LtE, a) }
 lt_bitwise_or[CmpopExprPair*]: '<' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, Lt, a) }
 gte_bitwise_or[CmpopExprPair*]: '>=' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, GtE, a) }
@@ -460,6 +459,7 @@ await_primary[expr_ty] (memo):
     | AWAIT a=primary { CHECK_VERSION(5, "Await expressions are", _Py_Await(a, EXTRA)) }
     | primary
 primary[expr_ty]:
+    | invalid_primary  # must be before 'primay genexp' because of invalid_genexp
     | a=primary '.' b=NAME { _Py_Attribute(a, b->v.Name.id, Load, EXTRA) }
     | a=primary b=genexp { _Py_Call(a, CHECK(_PyPegen_singleton_seq(p, b)), NULL, EXTRA) }
     | a=primary '(' b=[arguments] ')' {
@@ -502,11 +502,11 @@ group[expr_ty]:
     | '(' a=(yield_expr | named_expression) ')' { a }
     | invalid_group
 genexp[expr_ty]:
-    | '(' a=expression ~ b=for_if_clauses ')' { _Py_GeneratorExp(a, b, EXTRA) }
+    | '(' a=named_expression ~ b=for_if_clauses ')' { _Py_GeneratorExp(a, b, EXTRA) }
     | invalid_comprehension
-set[expr_ty]: '{' a=expressions_list '}' { _Py_Set(a, EXTRA) }
+set[expr_ty]: '{' a=star_named_expressions '}' { _Py_Set(a, EXTRA) }
 setcomp[expr_ty]:
-    | '{' a=expression ~ b=for_if_clauses '}' { _Py_SetComp(a, b, EXTRA) }
+    | '{' a=named_expression ~ b=for_if_clauses '}' { _Py_SetComp(a, b, EXTRA) }
     | invalid_comprehension
 dict[expr_ty]:
     | '{' a=[double_starred_kvpairs] '}' {
@@ -534,7 +534,7 @@ yield_expr[expr_ty]:
 
 arguments[expr_ty] (memo):
     | a=args [','] &')' { a }
-    | incorrect_arguments
+    | invalid_arguments
 args[expr_ty]:
     | a=','.(starred_expression | named_expression !'=')+ b=[',' k=kwargs {k}] { _PyPegen_collect_call_seqs(p, a, b, EXTRA) }
     | a=kwargs { _Py_Call(_PyPegen_dummy_name(p),
@@ -619,7 +619,7 @@ t_atom[expr_ty]:
 
 
 # From here on, there are rules for invalid syntax with specialised error messages
-incorrect_arguments:
+invalid_arguments:
     | args ',' '*' { RAISE_SYNTAX_ERROR("iterable argument unpacking follows keyword argument unpacking") }
     | a=expression for_if_clauses ',' [args | expression for_if_clauses] {
         RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "Generator expression must be parenthesized") }
@@ -664,6 +664,8 @@ invalid_del_stmt:
         RAISE_SYNTAX_ERROR_INVALID_TARGET(DEL_TARGETS, a) }
 invalid_block:
     | NEWLINE !INDENT { RAISE_INDENTATION_ERROR("expected an indented block") }
+invalid_primary:
+    | primary a='{' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "invalid syntax") }
 invalid_comprehension:
     | ('[' | '(' | '{') a=starred_expression for_if_clauses {
         RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "iterable unpacking cannot be used in comprehension") }
index 57eac13..3005ce1 100644 (file)
@@ -8,14 +8,6 @@ extern "C" {
 
 PyAPI_FUNC(char *) Py_UniversalNewlineFgets(char *, int, FILE*, PyObject *);
 
-#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03060000
-PyAPI_DATA(const char *) Py_FileSystemDefaultEncodeErrors;
-#endif
-
-#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03070000
-PyAPI_DATA(int) Py_UTF8Mode;
-#endif
-
 /* The std printer acts as a preliminary sys.stderr until the new io
    infrastructure is in place. */
 PyAPI_FUNC(PyObject *) PyFile_NewStdPrinter(int);
index 456887e..6ec2994 100644 (file)
@@ -20,8 +20,15 @@ PyAPI_FUNC(int) PyObject_AsFileDescriptor(PyObject *);
    If non-NULL, this is different than the default encoding for strings
 */
 PyAPI_DATA(const char *) Py_FileSystemDefaultEncoding;
+#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03060000
+PyAPI_DATA(const char *) Py_FileSystemDefaultEncodeErrors;
+#endif
 PyAPI_DATA(int) Py_HasFileSystemDefaultEncoding;
 
+#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03070000
+PyAPI_DATA(int) Py_UTF8Mode;
+#endif
+
 /* A routine to check if a file descriptor can be select()-ed. */
 #ifdef _MSC_VER
     /* On Windows, any socket fd can be select()-ed, no matter how high */
index 975e150..2b20fc6 100644 (file)
@@ -15,12 +15,9 @@ extern "C" {
 #  error "this header requires Py_BUILD_CORE define"
 #endif
 
-#if ((defined(__GNUC__) \
-      && ((__GNUC__ >= 5) || (__GNUC__ == 4) && (__GNUC_MINOR__ >= 8))) \
-     || (defined(__clang__) \
-         && (__clang_major__ >= 4 \
-             || (__clang_major__ == 3 && __clang_minor__ >= 2))))
-   /* __builtin_bswap16() is available since GCC 4.8 and clang 3.2,
+#if defined(__GNUC__) \
+      && ((__GNUC__ >= 5) || (__GNUC__ == 4) && (__GNUC_MINOR__ >= 8))
+   /* __builtin_bswap16() is available since GCC 4.8,
       __builtin_bswap32() is available since GCC 4.3,
       __builtin_bswap64() is available since GCC 4.3. */
 #  define _PY_HAVE_BUILTIN_BSWAP
@@ -34,7 +31,7 @@ extern "C" {
 static inline uint16_t
 _Py_bswap16(uint16_t word)
 {
-#ifdef _PY_HAVE_BUILTIN_BSWAP
+#if defined(_PY_HAVE_BUILTIN_BSWAP) || _Py__has_builtin(__builtin_bswap16)
     return __builtin_bswap16(word);
 #elif defined(_MSC_VER)
     Py_BUILD_ASSERT(sizeof(word) == sizeof(unsigned short));
@@ -49,7 +46,7 @@ _Py_bswap16(uint16_t word)
 static inline uint32_t
 _Py_bswap32(uint32_t word)
 {
-#ifdef _PY_HAVE_BUILTIN_BSWAP
+#if defined(_PY_HAVE_BUILTIN_BSWAP) || _Py__has_builtin(__builtin_bswap32)
     return __builtin_bswap32(word);
 #elif defined(_MSC_VER)
     Py_BUILD_ASSERT(sizeof(word) == sizeof(unsigned long));
@@ -66,7 +63,7 @@ _Py_bswap32(uint32_t word)
 static inline uint64_t
 _Py_bswap64(uint64_t word)
 {
-#ifdef _PY_HAVE_BUILTIN_BSWAP
+#if defined(_PY_HAVE_BUILTIN_BSWAP) || _Py__has_builtin(__builtin_bswap64)
     return __builtin_bswap64(word);
 #elif defined(_MSC_VER)
     return _byteswap_uint64(word);
index b76bb2c..50ab645 100644 (file)
@@ -68,6 +68,7 @@ extern void _PyFloat_Fini(void);
 extern void _PySlice_Fini(void);
 extern void _PyAsyncGen_Fini(void);
 
+extern int _PySignal_Init(int install_signal_handlers);
 extern void PyOS_FiniInterrupts(void);
 
 extern void _PyExc_Fini(void);
index a9e8ef1..0b5d280 100644 (file)
 /*--start constants--*/
 #define PY_MAJOR_VERSION        3
 #define PY_MINOR_VERSION        9
-#define PY_MICRO_VERSION        0
+#define PY_MICRO_VERSION        1
 #define PY_RELEASE_LEVEL        PY_RELEASE_LEVEL_FINAL
 #define PY_RELEASE_SERIAL       0
 
 /* Version as a string */
-#define PY_VERSION              "3.9.0"
+#define PY_VERSION              "3.9.1"
 /*--end constants--*/
 
 /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2.
index 6505af4..4bd4eb4 100644 (file)
@@ -863,4 +863,16 @@ extern _invalid_parameter_handler _Py_silent_invalid_parameter_handler;
 #  define _Py_NO_RETURN
 #endif
 
+
+// Preprocessor check for a builtin preprocessor function. Always return 0
+// if __has_builtin() macro is not defined.
+//
+// __has_builtin() is available on clang and GCC 10.
+#ifdef __has_builtin
+#  define _Py__has_builtin(x) __has_builtin(x)
+#else
+#  define _Py__has_builtin(x) 0
+#endif
+
+
 #endif /* Py_PYPORT_H */
index e9efce7..37975fe 100644 (file)
@@ -52,7 +52,7 @@ def _find_executable(executable, path=None):
         return executable
 
 
-def _read_output(commandstring):
+def _read_output(commandstring, capture_stderr=False):
     """Output from successful command execution or None"""
     # Similar to os.popen(commandstring, "r").read(),
     # but without actually using os.popen because that
@@ -67,7 +67,10 @@ def _read_output(commandstring):
             os.getpid(),), "w+b")
 
     with contextlib.closing(fp) as fp:
-        cmd = "%s 2>/dev/null >'%s'" % (commandstring, fp.name)
+        if capture_stderr:
+            cmd = "%s >'%s' 2>&1" % (commandstring, fp.name)
+        else:
+            cmd = "%s 2>/dev/null >'%s'" % (commandstring, fp.name)
         return fp.read().decode('utf-8').strip() if not os.system(cmd) else None
 
 
@@ -110,6 +113,26 @@ def _get_system_version():
 
     return _SYSTEM_VERSION
 
+_SYSTEM_VERSION_TUPLE = None
+def _get_system_version_tuple():
+    """
+    Return the macOS system version as a tuple
+
+    The return value is safe to use to compare
+    two version numbers.
+    """
+    global _SYSTEM_VERSION_TUPLE
+    if _SYSTEM_VERSION_TUPLE is None:
+        osx_version = _get_system_version()
+        if osx_version:
+            try:
+                _SYSTEM_VERSION_TUPLE = tuple(int(i) for i in osx_version.split('.'))
+            except ValueError:
+                _SYSTEM_VERSION_TUPLE = ()
+
+    return _SYSTEM_VERSION_TUPLE
+
+
 def _remove_original_values(_config_vars):
     """Remove original unmodified values for testing"""
     # This is needed for higher-level cross-platform tests of get_platform.
@@ -125,6 +148,33 @@ def _save_modified_value(_config_vars, cv, newvalue):
         _config_vars[_INITPRE + cv] = oldvalue
     _config_vars[cv] = newvalue
 
+
+_cache_default_sysroot = None
+def _default_sysroot(cc):
+    """ Returns the root of the default SDK for this system, or '/' """
+    global _cache_default_sysroot
+
+    if _cache_default_sysroot is not None:
+        return _cache_default_sysroot
+   
+    contents = _read_output('%s -c -E -v - </dev/null' % (cc,), True)
+    in_incdirs = False   
+    for line in contents.splitlines():
+        if line.startswith("#include <...>"):
+            in_incdirs = True
+        elif line.startswith("End of search list"):
+            in_incdirs = False
+        elif in_incdirs:
+            line = line.strip()
+            if line == '/usr/include':
+                _cache_default_sysroot = '/'
+            elif line.endswith(".sdk/usr/include"):
+                _cache_default_sysroot = line[:-12]
+    if _cache_default_sysroot is None:
+        _cache_default_sysroot = '/'
+
+    return _cache_default_sysroot
+
 def _supports_universal_builds():
     """Returns True if universal builds are supported on this system"""
     # As an approximation, we assume that if we are running on 10.4 or above,
@@ -132,14 +182,18 @@ def _supports_universal_builds():
     # builds, in particular -isysroot and -arch arguments to the compiler. This
     # is in support of allowing 10.4 universal builds to run on 10.3.x systems.
 
-    osx_version = _get_system_version()
-    if osx_version:
-        try:
-            osx_version = tuple(int(i) for i in osx_version.split('.'))
-        except ValueError:
-            osx_version = ''
+    osx_version = _get_system_version_tuple()
     return bool(osx_version >= (10, 4)) if osx_version else False
 
+def _supports_arm64_builds():
+    """Returns True if arm64 builds are supported on this system"""
+    # There are two sets of systems supporting macOS/arm64 builds:
+    # 1. macOS 11 and later, unconditionally
+    # 2. macOS 10.15 with Xcode 12.2 or later
+    # For now the second category is ignored.
+    osx_version = _get_system_version_tuple()
+    return osx_version >= (11, 0) if osx_version else False
+
 
 def _find_appropriate_compiler(_config_vars):
     """Find appropriate C compiler for extension module builds"""
@@ -331,6 +385,12 @@ def compiler_fixup(compiler_so, cc_args):
             except ValueError:
                 break
 
+    elif not _supports_arm64_builds():
+        # Look for "-arch arm64" and drop that
+        for idx in reversed(range(len(compiler_so))):
+            if compiler_so[idx] == '-arch' and compiler_so[idx+1] == "arm64":
+                del compiler_so[idx:idx+2]
+
     if 'ARCHFLAGS' in os.environ and not stripArch:
         # User specified different -arch flags in the environ,
         # see also distutils.sysconfig
@@ -481,6 +541,8 @@ def get_platform_osx(_config_vars, osname, release, machine):
 
             if len(archs) == 1:
                 machine = archs[0]
+            elif archs == ('arm64', 'x86_64'):
+                machine = 'universal2'
             elif archs == ('i386', 'ppc'):
                 machine = 'fat'
             elif archs == ('i386', 'x86_64'):
index d860917..ecd4895 100644 (file)
@@ -662,17 +662,23 @@ class _Precedence(IntEnum):
         except ValueError:
             return self
 
+
+_SINGLE_QUOTES = ("'", '"')
+_MULTI_QUOTES = ('"""', "'''")
+_ALL_QUOTES = (*_SINGLE_QUOTES, *_MULTI_QUOTES)
+
 class _Unparser(NodeVisitor):
     """Methods in this class recursively traverse an AST and
     output source code for the abstract syntax; original formatting
     is disregarded."""
 
-    def __init__(self):
+    def __init__(self, *, _avoid_backslashes=False):
         self._source = []
         self._buffer = []
         self._precedences = {}
         self._type_ignores = {}
         self._indent = 0
+        self._avoid_backslashes = _avoid_backslashes
 
     def interleave(self, inter, f, seq):
         """Call f on each item in seq, calling inter() in between."""
@@ -1067,15 +1073,85 @@ class _Unparser(NodeVisitor):
         with self.block(extra=self.get_type_comment(node)):
             self.traverse(node.body)
 
+    def _str_literal_helper(
+        self, string, *, quote_types=_ALL_QUOTES, escape_special_whitespace=False
+    ):
+        """Helper for writing string literals, minimizing escapes.
+        Returns the tuple (string literal to write, possible quote types).
+        """
+        def escape_char(c):
+            # \n and \t are non-printable, but we only escape them if
+            # escape_special_whitespace is True
+            if not escape_special_whitespace and c in "\n\t":
+                return c
+            # Always escape backslashes and other non-printable characters
+            if c == "\\" or not c.isprintable():
+                return c.encode("unicode_escape").decode("ascii")
+            return c
+
+        escaped_string = "".join(map(escape_char, string))
+        possible_quotes = quote_types
+        if "\n" in escaped_string:
+            possible_quotes = [q for q in possible_quotes if q in _MULTI_QUOTES]
+        possible_quotes = [q for q in possible_quotes if q not in escaped_string]
+        if not possible_quotes:
+            # If there aren't any possible_quotes, fallback to using repr
+            # on the original string. Try to use a quote from quote_types,
+            # e.g., so that we use triple quotes for docstrings.
+            string = repr(string)
+            quote = next((q for q in quote_types if string[0] in q), string[0])
+            return string[1:-1], [quote]
+        if escaped_string:
+            # Sort so that we prefer '''"''' over """\""""
+            possible_quotes.sort(key=lambda q: q[0] == escaped_string[-1])
+            # If we're using triple quotes and we'd need to escape a final
+            # quote, escape it
+            if possible_quotes[0][0] == escaped_string[-1]:
+                assert len(possible_quotes[0]) == 3
+                escaped_string = escaped_string[:-1] + "\\" + escaped_string[-1]
+        return escaped_string, possible_quotes
+
+    def _write_str_avoiding_backslashes(self, string, *, quote_types=_ALL_QUOTES):
+        """Write string literal value with a best effort attempt to avoid backslashes."""
+        string, quote_types = self._str_literal_helper(string, quote_types=quote_types)
+        quote_type = quote_types[0]
+        self.write(f"{quote_type}{string}{quote_type}")
+
     def visit_JoinedStr(self, node):
         self.write("f")
-        self._fstring_JoinedStr(node, self.buffer_writer)
-        self.write(repr(self.buffer))
+        if self._avoid_backslashes:
+            self._fstring_JoinedStr(node, self.buffer_writer)
+            self._write_str_avoiding_backslashes(self.buffer)
+            return
+
+        # If we don't need to avoid backslashes globally (i.e., we only need
+        # to avoid them inside FormattedValues), it's cosmetically preferred
+        # to use escaped whitespace. That is, it's preferred to use backslashes
+        # for cases like: f"{x}\n". To accomplish this, we keep track of what
+        # in our buffer corresponds to FormattedValues and what corresponds to
+        # Constant parts of the f-string, and allow escapes accordingly.
+        buffer = []
+        for value in node.values:
+            meth = getattr(self, "_fstring_" + type(value).__name__)
+            meth(value, self.buffer_writer)
+            buffer.append((self.buffer, isinstance(value, Constant)))
+        new_buffer = []
+        quote_types = _ALL_QUOTES
+        for value, is_constant in buffer:
+            # Repeatedly narrow down the list of possible quote_types
+            value, quote_types = self._str_literal_helper(
+                value, quote_types=quote_types,
+                escape_special_whitespace=is_constant
+            )
+            new_buffer.append(value)
+        value = "".join(new_buffer)
+        quote_type = quote_types[0]
+        self.write(f"{quote_type}{value}{quote_type}")
 
     def visit_FormattedValue(self, node):
         self.write("f")
         self._fstring_FormattedValue(node, self.buffer_writer)
-        self.write(repr(self.buffer))
+        self._write_str_avoiding_backslashes(self.buffer)
 
     def _fstring_JoinedStr(self, node, write):
         for value in node.values:
@@ -1090,11 +1166,13 @@ class _Unparser(NodeVisitor):
 
     def _fstring_FormattedValue(self, node, write):
         write("{")
-        unparser = type(self)()
+        unparser = type(self)(_avoid_backslashes=True)
         unparser.set_precedence(_Precedence.TEST.next(), node.value)
         expr = unparser.visit(node.value)
         if expr.startswith("{"):
             write(" ")  # Separate pair of opening brackets as "{ {"
+        if "\\" in expr:
+            raise ValueError("Unable to avoid backslash in f-string expression part")
         write(expr)
         if node.conversion != -1:
             conversion = chr(node.conversion)
@@ -1111,33 +1189,17 @@ class _Unparser(NodeVisitor):
         self.write(node.id)
 
     def _write_docstring(self, node):
-        def esc_char(c):
-            if c in ("\n", "\t"):
-                # In the AST form, we don't know the author's intentation
-                # about how this should be displayed. We'll only escape
-                # \n and \t, because they are more likely to be unescaped
-                # in the source
-                return c
-            return c.encode('unicode_escape').decode('ascii')
-
         self.fill()
         if node.kind == "u":
             self.write("u")
-
-        value = node.value
-        if value:
-            # Preserve quotes in the docstring by escaping them
-            value = "".join(map(esc_char, value))
-            if value[-1] == '"':
-                value = value.replace('"', '\\"', -1)
-            value = value.replace('"""', '""\\"')
-
-        self.write(f'"""{value}"""')
+        self._write_str_avoiding_backslashes(node.value, quote_types=_MULTI_QUOTES)
 
     def _write_constant(self, value):
         if isinstance(value, (float, complex)):
             # Substitute overflowing decimal literal for AST infinities.
             self.write(repr(value).replace("inf", _INFSTR))
+        elif self._avoid_backslashes and isinstance(value, str):
+            self._write_str_avoiding_backslashes(value)
         else:
             self.write(repr(value))
 
index 22f2980..2c01ac9 100644 (file)
@@ -1,6 +1,7 @@
 __all__ = ()
 
 import reprlib
+from _thread import get_ident
 
 from . import format_helpers
 
@@ -41,6 +42,16 @@ def _format_callbacks(cb):
     return f'cb=[{cb}]'
 
 
+# bpo-42183: _repr_running is needed for repr protection
+# when a Future or Task result contains itself directly or indirectly.
+# The logic is borrowed from @reprlib.recursive_repr decorator.
+# Unfortunately, the direct decorator usage is impossible because of
+# AttributeError: '_asyncio.Task' object has no attribute '__module__' error.
+#
+# After fixing this thing we can return to the decorator based approach.
+_repr_running = set()
+
+
 def _future_repr_info(future):
     # (Future) -> str
     """helper function for Future.__repr__"""
@@ -49,9 +60,17 @@ def _future_repr_info(future):
         if future._exception is not None:
             info.append(f'exception={future._exception!r}')
         else:
-            # use reprlib to limit the length of the output, especially
-            # for very long strings
-            result = reprlib.repr(future._result)
+            key = id(future), get_ident()
+            if key in _repr_running:
+                result = '...'
+            else:
+                _repr_running.add(key)
+                try:
+                    # use reprlib to limit the length of the output, especially
+                    # for very long strings
+                    result = reprlib.repr(future._result)
+                finally:
+                    _repr_running.discard(key)
             info.append(f'result={result}')
     if future._callbacks:
         info.append(_format_callbacks(future._callbacks))
index e03602e..f07e448 100644 (file)
@@ -34,8 +34,9 @@ class IncompleteReadError(EOFError):
     - expected: total number of expected bytes (or None if unknown)
     """
     def __init__(self, partial, expected):
+        r_expected = 'undefined' if expected is None else repr(expected)
         super().__init__(f'{len(partial)} bytes read on a total of '
-                         f'{expected!r} expected bytes')
+                         f'{r_expected} expected bytes')
         self.partial = partial
         self.expected = expected
 
index 8b05434..f486b67 100644 (file)
@@ -373,7 +373,7 @@ ALL_COMPLETED = concurrent.futures.ALL_COMPLETED
 async def wait(fs, *, loop=None, timeout=None, return_when=ALL_COMPLETED):
     """Wait for the Futures and coroutines given by fs to complete.
 
-    The sequence futures must not be empty.
+    The fs iterable must not be empty.
 
     Coroutines will be wrapped in Tasks.
 
@@ -400,13 +400,15 @@ async def wait(fs, *, loop=None, timeout=None, return_when=ALL_COMPLETED):
                       "and scheduled for removal in Python 3.10.",
                       DeprecationWarning, stacklevel=2)
 
-    if any(coroutines.iscoroutine(f) for f in set(fs)):
+    fs = set(fs)
+
+    if any(coroutines.iscoroutine(f) for f in fs):
         warnings.warn("The explicit passing of coroutine objects to "
                       "asyncio.wait() is deprecated since Python 3.8, and "
                       "scheduled for removal in Python 3.11.",
                       DeprecationWarning, stacklevel=2)
 
-    fs = {ensure_future(f, loop=loop) for f in set(fs)}
+    fs = {ensure_future(f, loop=loop) for f in fs}
 
     return await _wait(fs, timeout, return_when, loop)
 
@@ -573,7 +575,7 @@ def as_completed(fs, *, loop=None, timeout=None):
     Note: The futures 'f' are not necessarily members of fs.
     """
     if futures.isfuture(fs) or coroutines.iscoroutine(fs):
-        raise TypeError(f"expect a list of futures, not {type(fs).__name__}")
+        raise TypeError(f"expect an iterable of futures, not {type(fs).__name__}")
 
     from .queues import Queue  # Import here to avoid circular import problem.
     done = Queue(loop=loop)
index 9559f46..ace5217 100644 (file)
@@ -117,12 +117,12 @@ class _Hqxcoderengine:
         first = 0
         while first <= len(self.hqxdata) - self.linelen:
             last = first + self.linelen
-            self.ofp.write(self.hqxdata[first:last] + b'\n')
+            self.ofp.write(self.hqxdata[first:last] + b'\r')
             self.linelen = LINELEN
             first = last
         self.hqxdata = self.hqxdata[first:]
         if force:
-            self.ofp.write(self.hqxdata + b':\n')
+            self.ofp.write(self.hqxdata + b':\r')
 
     def close(self):
         if self.data:
index 4f20203..59b4699 100755 (executable)
@@ -152,6 +152,11 @@ def main():
     (options, args) = parser.parse_args()
     sys.argv[:] = args
 
+    # The script that we're profiling may chdir, so capture the absolute path
+    # to the output file at startup.
+    if options.outfile is not None:
+        options.outfile = os.path.abspath(options.outfile)
+
     if len(args) > 0:
         if options.module:
             code = "run_module(modname, run_name='__main__')"
index efd654e..bc69a67 100644 (file)
@@ -949,7 +949,7 @@ class ChainMap(_collections_abc.MutableMapping):
     def __iter__(self):
         d = {}
         for mapping in reversed(self.maps):
-            d.update(mapping)                   # reuses stored hash values if possible
+            d.update(dict.fromkeys(mapping))    # reuses stored hash values if possible
         return iter(d)
 
     def __contains__(self, key):
index dfc463c..7ab8c12 100644 (file)
@@ -48,6 +48,7 @@ def _reconstructor(cls, base, state):
     return obj
 
 _HEAPTYPE = 1<<9
+_new_type = type(int.__new__)
 
 # Python code for object.__reduce_ex__ for protocols 0 and 1
 
@@ -57,6 +58,9 @@ def _reduce_ex(self, proto):
     for base in cls.__mro__:
         if hasattr(base, '__flags__') and not base.__flags__ & _HEAPTYPE:
             break
+        new = base.__new__
+        if isinstance(new, _new_type) and new.__self__ is base:
+            break
     else:
         base = object # not really reachable
     if base is object:
index 9d86b05..1c3f8fd 100644 (file)
@@ -6,6 +6,11 @@ import os
 from ctypes.macholib.framework import framework_info
 from ctypes.macholib.dylib import dylib_info
 from itertools import *
+try:
+    from _ctypes import _dyld_shared_cache_contains_path
+except ImportError:
+    def _dyld_shared_cache_contains_path(*args):
+        raise NotImplementedError
 
 __all__ = [
     'dyld_find', 'framework_find',
@@ -122,8 +127,15 @@ def dyld_find(name, executable_path=None, env=None):
                 dyld_executable_path_search(name, executable_path),
                 dyld_default_search(name, env),
             ), env):
+
         if os.path.isfile(path):
             return path
+        try:
+            if _dyld_shared_cache_contains_path(path):
+                return path
+        except NotImplementedError:
+            pass
+
     raise ValueError("dylib %s could not be found" % (name,))
 
 def framework_find(fn, executable_path=None, env=None):
index b99fdcb..92ac184 100644 (file)
@@ -1,4 +1,5 @@
 import unittest
+import unittest.mock
 import os.path
 import sys
 import test.support
@@ -72,7 +73,7 @@ class Test_OpenGL_libs(unittest.TestCase):
 
 @unittest.skipUnless(sys.platform.startswith('linux'),
                      'Test only valid for Linux')
-class LibPathFindTest(unittest.TestCase):
+class FindLibraryLinux(unittest.TestCase):
     def test_find_on_libpath(self):
         import subprocess
         import tempfile
@@ -111,6 +112,15 @@ class LibPathFindTest(unittest.TestCase):
                 # LD_LIBRARY_PATH)
                 self.assertEqual(find_library(libname), 'lib%s.so' % libname)
 
+    def test_find_library_with_gcc(self):
+        with unittest.mock.patch("ctypes.util._findSoname_ldconfig", lambda *args: None):
+            self.assertNotEqual(find_library('c'), None)
+
+    def test_find_library_with_ld(self):
+        with unittest.mock.patch("ctypes.util._findSoname_ldconfig", lambda *args: None), \
+             unittest.mock.patch("ctypes.util._findLib_gcc", lambda *args: None):
+            self.assertNotEqual(find_library('c'), None)
+
 
 if __name__ == "__main__":
     unittest.main()
index 6b35269..a1bac26 100644 (file)
@@ -45,19 +45,22 @@ def find_lib(name):
 class MachOTest(unittest.TestCase):
     @unittest.skipUnless(sys.platform == "darwin", 'OSX-specific test')
     def test_find(self):
-
-        self.assertEqual(find_lib('pthread'),
-                             '/usr/lib/libSystem.B.dylib')
+        # On Mac OS 11, system dylibs are only present in the shared cache,
+        # so symlinks like libpthread.dylib -> libSystem.B.dylib will not
+        # be resolved by dyld_find
+        self.assertIn(find_lib('pthread'),
+                              ('/usr/lib/libSystem.B.dylib', '/usr/lib/libpthread.dylib'))
 
         result = find_lib('z')
         # Issue #21093: dyld default search path includes $HOME/lib and
         # /usr/local/lib before /usr/lib, which caused test failures if
         # a local copy of libz exists in one of them. Now ignore the head
         # of the path.
-        self.assertRegex(result, r".*/lib/libz\..*.*\.dylib")
+        self.assertRegex(result, r".*/lib/libz.*\.dylib")
 
-        self.assertEqual(find_lib('IOKit'),
-                             '/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit')
+        self.assertIn(find_lib('IOKit'),
+                              ('/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit',
+                              '/System/Library/Frameworks/IOKit.framework/IOKit'))
 
 if __name__ == "__main__":
     unittest.main()
index 71442df..243d596 100644 (file)
@@ -1,12 +1,13 @@
-import sys
 import unittest
 
+# also work on POSIX
+
 from ctypes import *
+from ctypes import wintypes
+
 
-@unittest.skipUnless(sys.platform.startswith('win'), 'Windows-only test')
 class WinTypesTest(unittest.TestCase):
     def test_variant_bool(self):
-        from ctypes import wintypes
         # reads 16-bits from memory, anything non-zero is True
         for true_value in (1, 32767, 32768, 65535, 65537):
             true = POINTER(c_int16)(c_int16(true_value))
@@ -37,5 +38,6 @@ class WinTypesTest(unittest.TestCase):
         vb.value = []
         self.assertIs(vb.value, False)
 
+
 if __name__ == "__main__":
     unittest.main()
index 01176bf..0c2510e 100644 (file)
@@ -93,6 +93,12 @@ elif os.name == "posix":
     # Andreas Degert's find functions, using gcc, /sbin/ldconfig, objdump
     import re, tempfile
 
+    def _is_elf(filename):
+        "Return True if the given file is an ELF file"
+        elf_header = b'\x7fELF'
+        with open(filename, 'br') as thefile:
+            return thefile.read(4) == elf_header
+
     def _findLib_gcc(name):
         # Run GCC's linker with the -t (aka --trace) option and examine the
         # library name it prints out. The GCC command will fail because we
@@ -130,10 +136,17 @@ elif os.name == "posix":
                 # Raised if the file was already removed, which is the normal
                 # behaviour of GCC if linking fails
                 pass
-        res = re.search(expr, trace)
+        res = re.findall(expr, trace)
         if not res:
             return None
-        return os.fsdecode(res.group(0))
+
+        for file in res:
+            # Check if the given file is an elf file: gcc can report
+            # some files that are linker scripts and not actual
+            # shared objects. See bpo-41976 for more details
+            if not _is_elf(file):
+                continue
+            return os.fsdecode(file)
 
 
     if sys.platform == "sunos5":
@@ -299,9 +312,14 @@ elif os.name == "posix":
                                      stderr=subprocess.PIPE,
                                      universal_newlines=True)
                 out, _ = p.communicate()
-                res = re.search(expr, os.fsdecode(out))
-                if res:
-                    result = res.group(0)
+                res = re.findall(expr, os.fsdecode(out))
+                for file in res:
+                    # Check if the given file is an elf file: gcc can report
+                    # some files that are linker scripts and not actual
+                    # shared objects. See bpo-41976 for more details
+                    if not _is_elf(file):
+                        continue
+                    return os.fsdecode(file)
             except Exception:
                 pass  # result will be None
             return result
@@ -309,7 +327,7 @@ elif os.name == "posix":
         def find_library(name):
             # See issue #9998
             return _findSoname_ldconfig(name) or \
-                   _get_soname(_findLib_gcc(name) or _findLib_ld(name))
+                   _get_soname(_findLib_gcc(name)) or _get_soname(_findLib_ld(name))
 
 ################################################################
 # test code
index 2294ac2..e508d99 100644 (file)
@@ -1579,7 +1579,7 @@ class time:
         self._tzinfo = tzinfo
 
     def __reduce_ex__(self, protocol):
-        return (time, self._getstate(protocol))
+        return (self.__class__, self._getstate(protocol))
 
     def __reduce__(self):
         return self.__reduce_ex__(2)
index 0d1bd03..f50edd2 100644 (file)
@@ -54,8 +54,8 @@ def spawn(cmd, search_path=1, verbose=0, dry_run=0):
         global _cfg_target, _cfg_target_split
         if _cfg_target is None:
             from distutils import sysconfig
-            _cfg_target = sysconfig.get_config_var(
-                                  'MACOSX_DEPLOYMENT_TARGET') or ''
+            _cfg_target = str(sysconfig.get_config_var(
+                                  'MACOSX_DEPLOYMENT_TARGET') or '')
             if _cfg_target:
                 _cfg_target_split = [int(x) for x in _cfg_target.split('.')]
         if _cfg_target:
index 5e47e07..1b034c9 100644 (file)
@@ -455,7 +455,7 @@ class BuildExtTestCase(TempdirManager,
         deptarget = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET')
         if deptarget:
             # increment the minor version number (i.e. 10.6 -> 10.7)
-            deptarget = [int(x) for x in deptarget.split('.')]
+            deptarget = [int(x) for x in str(deptarget).split('.')]
             deptarget[-1] += 1
             deptarget = '.'.join(str(i) for i in deptarget)
             self._try_compile_deployment_target('<', deptarget)
@@ -488,16 +488,20 @@ class BuildExtTestCase(TempdirManager,
 
         # get the deployment target that the interpreter was built with
         target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET')
-        target = tuple(map(int, target.split('.')[0:2]))
+        target = tuple(map(int, str(target).split('.')[0:2]))
         # format the target value as defined in the Apple
         # Availability Macros.  We can't use the macro names since
         # at least one value we test with will not exist yet.
-        if target[1] < 10:
+        if target[:2] < (10, 10):
             # for 10.1 through 10.9.x -> "10n0"
             target = '%02d%01d0' % target
         else:
             # for 10.10 and beyond -> "10nn00"
-            target = '%02d%02d00' % target
+            if len(target) >= 2:
+                target = '%02d%02d00' % target
+            else:
+                # 11 and later can have no minor version (11 instead of 11.0)
+                target = '%02d0000' % target
         deptarget_ext = Extension(
             'deptarget',
             [deptarget_c],
index 4d7a6de..f0792de 100644 (file)
@@ -290,7 +290,7 @@ class UnixCCompiler(CCompiler):
             cflags = sysconfig.get_config_var('CFLAGS')
             m = re.search(r'-isysroot\s*(\S+)', cflags)
             if m is None:
-                sysroot = '/'
+                sysroot = _osx_support._default_sysroot(sysconfig.get_config_var('CC'))
             else:
                 sysroot = m.group(1)
 
index ae670c2..c9b1216 100644 (file)
@@ -186,7 +186,11 @@ class Generator:
         # If we munged the cte, copy the message again and re-fix the CTE.
         if munge_cte:
             msg = deepcopy(msg)
-            msg.replace_header('content-transfer-encoding', munge_cte[0])
+            # Preserve the header order if the CTE header already exists.
+            if msg.get('content-transfer-encoding') is None:
+                msg['Content-Transfer-Encoding'] = munge_cte[0]
+            else:
+                msg.replace_header('content-transfer-encoding', munge_cte[0])
             msg.replace_header('content-type', munge_cte[1])
         # Write the headers.  First we see if the message object wants to
         # handle that itself.  If not, we'll do it generically.
diff --git a/Lib/ensurepip/_bundled/pip-20.2.1-py2.py3-none-any.whl b/Lib/ensurepip/_bundled/pip-20.2.1-py2.py3-none-any.whl
deleted file mode 100644 (file)
index 3d0d3f8..0000000
Binary files a/Lib/ensurepip/_bundled/pip-20.2.1-py2.py3-none-any.whl and /dev/null differ
index 948f62f..d0e7a48 100644 (file)
@@ -22,7 +22,8 @@ class _NodeInfo:
 
 
 class CycleError(ValueError):
-    """Subclass of ValueError raised by TopologicalSorterif cycles exist in the graph
+    """Subclass of ValueError raised by TopologicalSorter.prepare if cycles
+    exist in the working graph.
 
     If multiple cycles exist, only one undefined choice among them will be reported
     and included in the exception. The detected cycle can be accessed via the second
@@ -129,7 +130,7 @@ class TopologicalSorter:
         return result
 
     def is_active(self):
-        """Return True if more progress can be made and ``False`` otherwise.
+        """Return ``True`` if more progress can be made and ``False`` otherwise.
 
         Progress can be made if cycles do not block the resolution and either there
         are still nodes ready that haven't yet been returned by "get_ready" or the
@@ -149,7 +150,7 @@ class TopologicalSorter:
         """Marks a set of nodes returned by "get_ready" as processed.
 
         This method unblocks any successor of each node in *nodes* for being returned
-        in the future by a a call to "get_ready"
+        in the future by a call to "get_ready".
 
         Raises :exec:`ValueError` if any node in *nodes* has already been marked as
         processed by a previous call to this method, if a node was not added to the
index fa204fb..def05f4 100644 (file)
@@ -1123,12 +1123,7 @@ class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
         referer = self.headers.get('referer')
         if referer:
             env['HTTP_REFERER'] = referer
-        accept = []
-        for line in self.headers.getallmatchingheaders('accept'):
-            if line[:1] in "\t\n\r ":
-                accept.append(line.strip())
-            else:
-                accept = accept + line[7:].split(',')
+        accept = self.headers.get_all('accept', ())
         env['HTTP_ACCEPT'] = ','.join(accept)
         ua = self.headers.get('user-agent')
         if ua:
index 8b47162..d91c4d5 100644 (file)
@@ -7,3 +7,7 @@ https://www.doxdesk.com/software/py/pyicons.html
 
 Various different formats and sizes are available at this GitHub Pull Request:
 https://github.com/python/cpython/pull/17473
+
+The idle.ico file was created with ImageMagick:
+
+    $ convert idle_16.png idle_32.png idle_48.png idle_256.png idle.ico
index 3357aef..2aa9a83 100644 (file)
Binary files a/Lib/idlelib/Icons/idle.ico and b/Lib/idlelib/Icons/idle.ico differ
index fd76207..869be0a 100644 (file)
@@ -1,8 +1,32 @@
+What's New in IDLE 3.9.1
+Released on 2020-12-07?
+======================================
+
+
+bpo-42508: Keep IDLE running on macOS.  Remove obsolete workaround
+that prevented running files with shortcuts when using new universal2
+installers built on macOS 11.
+
+bpo-42426: Fix reporting offset of the RE error in searchengine.
+
+bpo-42416: Get docstrings for IDLE calltips more often
+by using inspect.getdoc.
+
+bpo-33987: Mostly finish using ttk widgets, mainly for editor,
+settings, and searches.  Some patches by Mark Roseman.
+
+bpo-41775: Make 'IDLE Shell' the shell title.
+
+bpo-35764: Rewrite the Calltips doc section.
+
+bpo-40181: In calltips, stop reminding that '/' marks the end of
+positional-only arguments.
+
+
 What's New in IDLE 3.9.0 (since 3.8.0)
 Released on 2020-10-05?
 ======================================
 
-
 bpo-41468: Improve IDLE run crash error message (which users should
 never see).
 
index d4092c7..40bc5a0 100644 (file)
@@ -55,18 +55,50 @@ class Calltip:
             self.open_calltip(False)
 
     def open_calltip(self, evalfuncs):
-        self.remove_calltip_window()
+        """Maybe close an existing calltip and maybe open a new calltip.
 
+        Called from (force_open|try_open|refresh)_calltip_event functions.
+        """
         hp = HyperParser(self.editwin, "insert")
         sur_paren = hp.get_surrounding_brackets('(')
+
+        # If not inside parentheses, no calltip.
         if not sur_paren:
+            self.remove_calltip_window()
             return
+
+        # If a calltip is shown for the current parentheses, do
+        # nothing.
+        if self.active_calltip:
+            opener_line, opener_col = map(int, sur_paren[0].split('.'))
+            if (
+                (opener_line, opener_col) ==
+                (self.active_calltip.parenline, self.active_calltip.parencol)
+            ):
+                return
+
         hp.set_index(sur_paren[0])
-        expression  = hp.get_expression()
+        try:
+            expression = hp.get_expression()
+        except ValueError:
+            expression = None
         if not expression:
+            # No expression before the opening parenthesis, e.g.
+            # because it's in a string or the opener for a tuple:
+            # Do nothing.
             return
+
+        # At this point, the current index is after an opening
+        # parenthesis, in a section of code, preceded by a valid
+        # expression. If there is a calltip shown, it's not for the
+        # same index and should be closed.
+        self.remove_calltip_window()
+
+        # Simple, fast heuristic: If the preceding expression includes
+        # an opening parenthesis, it likely includes a function call.
         if not evalfuncs and (expression.find('(') != -1):
             return
+
         argspec = self.fetch_tip(expression)
         if not argspec:
             return
@@ -118,7 +150,6 @@ _INDENT = ' '*4  # for wrapped signatures
 _first_param = re.compile(r'(?<=\()\w*\,?\s*')
 _default_callable_argspec = "See source or doc"
 _invalid_method = "invalid method signature"
-_argument_positional = "  # '/' marks preceding args as positional-only."
 
 def get_argspec(ob):
     '''Return a string describing the signature of a callable object, or ''.
@@ -134,6 +165,7 @@ def get_argspec(ob):
         ob_call = ob.__call__
     except BaseException:  # Buggy user object could raise anything.
         return ''  # No popup for non-callables.
+    # For Get_argspecTest.test_buggy_getattr_class, CallA() & CallB().
     fob = ob_call if isinstance(ob_call, types.MethodType) else ob
 
     # Initialize argspec and wrap it to get lines.
@@ -146,9 +178,6 @@ def get_argspec(ob):
         else:
             argspec = ''
 
-    if '/' in argspec and len(argspec) < _MAX_COLS - len(_argument_positional):
-        # Add explanation TODO remove after 3.7, before 3.9.
-        argspec += _argument_positional
     if isinstance(fob, type) and argspec == '()':
         # If fob has no argument, use default callable argspec.
         argspec = _default_callable_argspec
@@ -157,10 +186,7 @@ def get_argspec(ob):
              if len(argspec) > _MAX_COLS else [argspec] if argspec else [])
 
     # Augment lines from docstring, if any, and join to get argspec.
-    if isinstance(ob_call, types.MethodType):
-        doc = ob_call.__doc__
-    else:
-        doc = getattr(ob, "__doc__", "")
+    doc = inspect.getdoc(ob)
     if doc:
         for line in doc.split('\n', _MAX_LINES)[:_MAX_LINES]:
             line = line.strip()
index 8259649..a84e1c5 100644 (file)
@@ -67,7 +67,6 @@ class ConfigDialog(Toplevel):
         if not _utest:
             self.withdraw()
 
-        self.configure(borderwidth=5)
         self.title(title or 'IDLE Preferences')
         x = parent.winfo_rootx() + 20
         y = parent.winfo_rooty() + (30 if not _htest else 150)
@@ -97,6 +96,7 @@ class ConfigDialog(Toplevel):
         """Create and place widgets for tabbed dialog.
 
         Widgets Bound to self:
+            frame: encloses all other widgets
             note: Notebook
             highpage: HighPage
             fontpage: FontPage
@@ -109,7 +109,9 @@ class ConfigDialog(Toplevel):
             load_configs: Load pages except for extensions.
             activate_config_changes: Tell editors to reload.
         """
-        self.note = note = Notebook(self)
+        self.frame = frame = Frame(self, padding="5px")
+        self.frame.grid(sticky="nwes")
+        self.note = note = Notebook(frame)
         self.highpage = HighPage(note)
         self.fontpage = FontPage(note, self.highpage)
         self.keyspage = KeysPage(note)
@@ -148,7 +150,7 @@ class ConfigDialog(Toplevel):
             padding_args = {}
         else:
             padding_args = {'padding': (6, 3)}
-        outer = Frame(self, padding=2)
+        outer = Frame(self.frame, padding=2)
         buttons_frame = Frame(outer, padding=2)
         self.buttons = {}
         for txt, cmd in (
@@ -687,7 +689,7 @@ class HighPage(Frame):
 
     def __init__(self, master):
         super().__init__(master)
-        self.cd = master.master
+        self.cd = master.winfo_toplevel()
         self.style = Style(master)
         self.create_page_highlight()
         self.load_theme_cfg()
@@ -1346,7 +1348,7 @@ class KeysPage(Frame):
 
     def __init__(self, master):
         super().__init__(master)
-        self.cd = master.master
+        self.cd = master.winfo_toplevel()
         self.create_page_keys()
         self.load_key_cfg()
 
index b2853cf..170999e 100644 (file)
@@ -1,23 +1,24 @@
 
 <!DOCTYPE html>
 
-<html xmlns="http://www.w3.org/1999/xhtml">
+<html>
   <head>
     <meta charset="utf-8" />
-    <title>IDLE &#8212; Python 3.10.0a0 documentation</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>IDLE &#8212; Python 3.10.0a1 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" 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 type="text/javascript" src="../_static/language_data.js"></script>
+    <script id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
+    <script src="../_static/jquery.js"></script>
+    <script src="../_static/underscore.js"></script>
+    <script src="../_static/doctools.js"></script>
+    <script src="../_static/language_data.js"></script>
 
-    <script type="text/javascript" src="../_static/sidebar.js"></script>
+    <script src="../_static/sidebar.js"></script>
 
     <link rel="search" type="application/opensearchdescription+xml"
-          title="Search within Python 3.10.0a0 documentation"
+          title="Search within Python 3.10.0a1 documentation"
           href="../_static/opensearch.xml"/>
     <link rel="author" title="About these documents" href="../about.html" />
     <link rel="index" title="Index" href="../genindex.html" />
 
 
     <li>
-      <a href="../index.html">3.10.0a0 Documentation</a> &#187;
+      <a href="../index.html">3.10.0a1 Documentation</a> &#187;
     </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">Graphical User Interfaces with Tk</a> &#187;</li>
+        <li class="nav-item nav-item-this"><a href="">IDLE</a></li>
     <li class="right">
 
 
@@ -426,30 +428,30 @@ and that other files do not.  Run Python code with the Run menu.</p>
 the <kbd class="kbd docutils literal notranslate">Command</kbd> key on macOS.</p>
 <ul>
 <li><p><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>
-<li><p><kbd class="kbd docutils literal notranslate">C-Backspace</kbd> delete word left; <kbd class="kbd docutils literal notranslate">C-Del</kbd> delete word to the right</p></li>
-<li><p>Arrow keys and <kbd class="kbd docutils literal notranslate">Page Up</kbd>/<kbd class="kbd docutils literal notranslate">Page Down</kbd> to move around</p></li>
-<li><p><kbd class="kbd docutils literal notranslate">C-LeftArrow</kbd> and <kbd class="kbd docutils literal notranslate">C-RightArrow</kbd> moves by words</p></li>
+<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">Backspace</kbd></kbd> delete word left; <kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">Del</kbd></kbd> delete word to the right</p></li>
+<li><p>Arrow keys and <kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">Page</kbd> <kbd class="kbd docutils literal notranslate">Up</kbd></kbd>/<kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">Page</kbd> <kbd class="kbd docutils literal notranslate">Down</kbd></kbd> to move around</p></li>
+<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">LeftArrow</kbd></kbd> and <kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">RightArrow</kbd></kbd> moves by words</p></li>
 <li><p><kbd class="kbd docutils literal notranslate">Home</kbd>/<kbd class="kbd docutils literal notranslate">End</kbd> go to begin/end of line</p></li>
-<li><p><kbd class="kbd docutils literal notranslate">C-Home</kbd>/<kbd class="kbd docutils literal notranslate">C-End</kbd> go to begin/end of file</p></li>
+<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">Home</kbd></kbd>/<kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">End</kbd></kbd> go to begin/end of file</p></li>
 <li><p>Some useful Emacs bindings are inherited from Tcl/Tk:</p>
 <blockquote>
 <div><ul class="simple">
-<li><p><kbd class="kbd docutils literal notranslate">C-a</kbd> beginning of line</p></li>
-<li><p><kbd class="kbd docutils literal notranslate">C-e</kbd> end of line</p></li>
-<li><p><kbd class="kbd docutils literal notranslate">C-k</kbd> kill line (but doesn’t put it in clipboard)</p></li>
-<li><p><kbd class="kbd docutils literal notranslate">C-l</kbd> center window around the insertion point</p></li>
-<li><p><kbd class="kbd docutils literal notranslate">C-b</kbd> go backward one character without deleting (usually you can
+<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">a</kbd></kbd> beginning of line</p></li>
+<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">e</kbd></kbd> end of line</p></li>
+<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">k</kbd></kbd> kill line (but doesn’t put it in clipboard)</p></li>
+<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">l</kbd></kbd> center window around the insertion point</p></li>
+<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">b</kbd></kbd> go backward one character without deleting (usually you can
 also use the cursor key for this)</p></li>
-<li><p><kbd class="kbd docutils literal notranslate">C-f</kbd> go forward one character without deleting (usually you can
+<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">f</kbd></kbd> go forward one character without deleting (usually you can
 also use the cursor key for this)</p></li>
-<li><p><kbd class="kbd docutils literal notranslate">C-p</kbd> go up one line (usually you can also use the cursor key for
+<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">p</kbd></kbd> go up one line (usually you can also use the cursor key for
 this)</p></li>
-<li><p><kbd class="kbd docutils literal notranslate">C-d</kbd> delete next character</p></li>
+<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">d</kbd></kbd> delete next character</p></li>
 </ul>
 </div></blockquote>
 </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)
+<p>Standard keybindings (like <kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">c</kbd></kbd> to copy and <kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">v</kbd></kbd> to paste)
 may work.  Keybindings are selected in the Configure IDLE dialog.</p>
 </div>
 <div class="section" id="automatic-indentation">
@@ -486,7 +488,7 @@ one can specify a drive first.)  Move into subdirectories by typing a
 directory name and a separator.</p>
 <p>Instead of waiting, or after a box is closed, open a completion box
 immediately with Show Completions on the Edit menu.  The default hot
-key is <kbd class="kbd docutils literal notranslate">C-space</kbd>.  If one types a prefix for the desired name
+key is <kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">space</kbd></kbd>.  If one types a prefix for the desired name
 before opening the box, the first match or near miss is made visible.
 The result is the same as if one enters a prefix
 after the box is displayed.  Show Completions after a quote completes
@@ -509,26 +511,29 @@ by typing ‘_’ after ‘.’, either before or after the box is opened.</p>
 </div>
 <div class="section" id="calltips">
 <span id="id4"></span><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,
-or <kbd class="kbd docutils literal notranslate">)</kbd> is typed.  When the cursor is in the argument part of a definition,
-the menu or shortcut display a calltip.</p>
-<p>A calltip consists of the function signature and the first line of the
-docstring.  For builtins without an accessible signature, the calltip
-consists of all lines up the fifth line or the first blank line.  These
-details may change.</p>
-<p>The set of <em>accessible</em> functions depends on what modules have been imported
-into the user process, including those imported by Idle itself,
-and what definitions have been run, all since the last restart.</p>
+<p>A calltip is shown automatically when one types <kbd class="kbd docutils literal notranslate">(</kbd> after the name
+of an <em>accessible</em> function.  A function name expression may include
+dots and subscripts.  A calltip remains until it is clicked, the cursor
+is moved out of the argument area, or <kbd class="kbd docutils literal notranslate">)</kbd> is typed.  Whenever the
+cursor is in the argument part of a definition, select Edit and “Show
+Call Tip” on the menu or enter its shortcut to display a calltip.</p>
+<p>The calltip consists of the function’s signature and docstring up to
+the latter’s first blank line or the fifth non-blank line.  (Some builtin
+functions lack an accessible signature.)  A ‘/’ or ‘*’ in the signature
+indicates that the preceding or following arguments are passed by
+position or name (keyword) only.  Details are subject to change.</p>
+<p>In Shell, the accessible functions depends on what modules have been
+imported into the user process, including those imported by Idle itself,
+and which definitions have been run, all since the last restart.</p>
 <p>For example, restart the Shell and enter <code class="docutils literal notranslate"><span class="pre">itertools.count(</span></code>.  A calltip
-appears because Idle imports itertools into the user process for its own use.
-(This could change.)  Enter <code class="docutils literal notranslate"><span class="pre">turtle.write(</span></code> and nothing appears.  Idle does
-not import turtle.  The menu or shortcut do nothing either.  Enter
-<code class="docutils literal notranslate"><span class="pre">import</span> <span class="pre">turtle</span></code> and then <code class="docutils literal notranslate"><span class="pre">turtle.write(</span></code> will work.</p>
-<p>In an editor, import statements have no effect until one runs the file.  One
-might want to run a file after writing the import statements at the top,
-or immediately run an existing file before editing.</p>
+appears because Idle imports itertools into the user process for its own
+use.  (This could change.)  Enter <code class="docutils literal notranslate"><span class="pre">turtle.write(</span></code> and nothing appears.
+Idle does not itself import turtle.  The menu entry and shortcut also do
+nothing.  Enter <code class="docutils literal notranslate"><span class="pre">import</span> <span class="pre">turtle</span></code>.  Thereafter, <code class="docutils literal notranslate"><span class="pre">turtle.write(</span></code>
+will display a calltip.</p>
+<p>In an editor, import statements have no effect until one runs the file.
+One might want to run a file after writing import statements, after
+adding function definitions, or after opening an existing file.</p>
 </div>
 <div class="section" id="code-context">
 <span id="id5"></span><h3>Code Context<a class="headerlink" href="#code-context" title="Permalink to this headline">¶</a></h3>
@@ -556,14 +561,14 @@ If one pastes more that one statement into Shell, the result will be a
 <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><kbd class="kbd docutils literal notranslate">C-c</kbd> interrupts executing command</p></li>
-<li><p><kbd class="kbd docutils literal notranslate">C-d</kbd> sends end-of-file; closes window if typed at a <code class="docutils literal notranslate"><span class="pre">&gt;&gt;&gt;</span></code> prompt</p></li>
-<li><p><kbd class="kbd docutils literal notranslate">Alt-/</kbd> (Expand word) is also useful to reduce typing</p>
+<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">c</kbd></kbd> interrupts executing command</p></li>
+<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">d</kbd></kbd> sends end-of-file; closes window if typed at a <code class="docutils literal notranslate"><span class="pre">&gt;&gt;&gt;</span></code> prompt</p></li>
+<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">Alt</kbd>-<kbd class="kbd docutils literal notranslate">/</kbd></kbd> (Expand word) is also useful to reduce typing</p>
 <p>Command history</p>
 <ul class="simple">
-<li><p><kbd class="kbd docutils literal notranslate">Alt-p</kbd> retrieves previous command matching what you have typed. On
-macOS use <kbd class="kbd docutils literal notranslate">C-p</kbd>.</p></li>
-<li><p><kbd class="kbd docutils literal notranslate">Alt-n</kbd> retrieves next. On macOS use <kbd class="kbd docutils literal notranslate">C-n</kbd>.</p></li>
+<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">Alt</kbd>-<kbd class="kbd docutils literal notranslate">p</kbd></kbd> retrieves previous command matching what you have typed. On
+macOS use <kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">p</kbd></kbd>.</p></li>
+<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">Alt</kbd>-<kbd class="kbd docutils literal notranslate">n</kbd></kbd> retrieves next. On macOS use <kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">n</kbd></kbd>.</p></li>
 <li><p><kbd class="kbd docutils literal notranslate">Return</kbd> while on any previous command retrieves that command</p></li>
 </ul>
 </li>
@@ -849,6 +854,7 @@ also used for testing.</p>
 </div>
 
 
+            <div class="clearer"></div>
           </div>
         </div>
       </div>
@@ -944,11 +950,12 @@ also used for testing.</p>
 
 
     <li>
-      <a href="../index.html">3.10.0a0 Documentation</a> &#187;
+      <a href="../index.html">3.10.0a1 Documentation</a> &#187;
     </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" >Graphical User Interfaces with Tk</a> &#187;</li>
+        <li class="nav-item nav-item-this"><a href="">IDLE</a></li>
     <li class="right">
 
 
@@ -975,11 +982,11 @@ also used for testing.</p>
 <br />
     <br />
 
-    Last updated on Sep 09, 2020.
+    Last updated on Oct 20, 2020.
     <a href="https://docs.python.org/3/bugs.html">Found a bug</a>?
     <br />
 
-    Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 2.1.1.
+    Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
     </div>
 
   </body>
index 9f63ea0..f420d40 100644 (file)
@@ -28,8 +28,8 @@ from html.parser import HTMLParser
 from os.path import abspath, dirname, isfile, join
 from platform import python_version
 
-from tkinter import Toplevel, Frame, Text, Menu
-from tkinter.ttk import Menubutton, Scrollbar
+from tkinter import Toplevel, Text, Menu
+from tkinter.ttk import Frame, Menubutton, Scrollbar, Style
 from tkinter import font as tkfont
 
 from idlelib.config import idleConf
@@ -212,7 +212,9 @@ class HelpFrame(Frame):
     def __init__(self, parent, filename):
         Frame.__init__(self, parent)
         self.text = text = HelpText(self, filename)
-        self['background'] = text['background']
+        self.style = Style(parent)
+        self['style'] = 'helpframe.TFrame'
+        self.style.configure('helpframe.TFrame', background=text['background'])
         self.toc = toc = self.toc_menu(text)
         self.scroll = scroll = Scrollbar(self, command=text.yview)
         text['yscrollcommand'] = scroll.set
index 576f7d5..b736bd0 100644 (file)
@@ -3,6 +3,9 @@
 A gui object is anything with a master or parent parameter, which is
 typically required in spite of what the doc strings say.
 """
+import re
+from _tkinter import TclError
+
 
 class Event:
     '''Minimal mock with attributes for testing event handlers.
@@ -22,6 +25,7 @@ class Event:
         "Create event with attributes needed for test"
         self.__dict__.update(kwds)
 
+
 class Var:
     "Use for String/Int/BooleanVar: incomplete"
     def __init__(self, master=None, value=None, name=None):
@@ -33,6 +37,7 @@ class Var:
     def get(self):
         return self.value
 
+
 class Mbox_func:
     """Generic mock for messagebox functions, which all have the same signature.
 
@@ -50,6 +55,7 @@ class Mbox_func:
         self.kwds = kwds
         return self.result  # Set by tester for ask functions
 
+
 class Mbox:
     """Mock for tkinter.messagebox with an Mbox_func for each function.
 
@@ -85,7 +91,6 @@ class Test(unittest.TestCase):
     showinfo = Mbox_func()     # None
     showwarning = Mbox_func()  # None
 
-from _tkinter import TclError
 
 class Text:
     """A semi-functional non-gui replacement for tkinter.Text text editors.
@@ -154,6 +159,8 @@ class Text:
         if char.endswith(' lineend') or char == 'end':
             return line, linelength
             # Tk requires that ignored chars before ' lineend' be valid int
+        if m := re.fullmatch(r'end-(\d*)c', char, re.A):  # Used by hyperparser.
+            return line, linelength - int(m.group(1))
 
         # Out of bounds char becomes first or last index of line
         char = int(char)
@@ -177,7 +184,6 @@ class Text:
             n -= 1
             return n, len(self.data[n]) + endflag
 
-
     def insert(self, index, chars):
         "Insert chars before the character at index."
 
@@ -193,7 +199,6 @@ class Text:
         self.data[line+1:line+1] = chars[1:]
         self.data[line+len(chars)-1] += after
 
-
     def get(self, index1, index2=None):
         "Return slice from index1 to index2 (default is 'index1+1')."
 
@@ -212,7 +217,6 @@ class Text:
             lines.append(self.data[endline][:endchar])
             return ''.join(lines)
 
-
     def delete(self, index1, index2=None):
         '''Delete slice from index1 to index2 (default is 'index1+1').
 
@@ -297,6 +301,7 @@ class Text:
         "Bind to this widget at event sequence a call to function func."
         pass
 
+
 class Entry:
     "Mock for tkinter.Entry."
     def focus_set(self):
index d386b5c..a76829f 100644 (file)
@@ -1,10 +1,12 @@
-"Test calltip, coverage 60%"
+"Test calltip, coverage 76%"
 
 from idlelib import calltip
 import unittest
+from unittest.mock import Mock
 import textwrap
 import types
 import re
+from idlelib.idle_test.mock_tk import Text
 
 
 # Test Class TC is used in multiple get_argspec test methods
@@ -61,18 +63,16 @@ class Get_argspecTest(unittest.TestCase):
 
         if List.__doc__ is not None:
             tiptest(List,
-                    f'(iterable=(), /){calltip._argument_positional}'
+                    f'(iterable=(), /)'
                     f'\n{List.__doc__}')
         tiptest(list.__new__,
               '(*args, **kwargs)\n'
               'Create and return a new object.  '
               'See help(type) for accurate signature.')
         tiptest(list.__init__,
-              '(self, /, *args, **kwargs)'
-              + calltip._argument_positional + '\n' +
+              '(self, /, *args, **kwargs)\n'
               'Initialize self.  See help(type(self)) for accurate signature.')
-        append_doc = (calltip._argument_positional
-                      + "\nAppend object to the end of the list.")
+        append_doc = "\nAppend object to the end of the list."
         tiptest(list.append, '(self, object, /)' + append_doc)
         tiptest(List.append, '(self, object, /)' + append_doc)
         tiptest([].append, '(object, /)' + append_doc)
@@ -99,7 +99,12 @@ non-overlapping occurrences o...''')
 (width=70, initial_indent='', subsequent_indent='', expand_tabs=True,
     replace_whitespace=True, fix_sentence_endings=False, break_long_words=True,
     drop_whitespace=True, break_on_hyphens=True, tabsize=8, *, max_lines=None,
-    placeholder=' [...]')''')
+    placeholder=' [...]')
+Object for wrapping/filling text.  The public interface consists of
+the wrap() and fill() methods; the other methods are just there for
+subclasses to override in order to tweak the default behaviour.
+If you want to completely replace the main wrapping algorithm,
+you\'ll probably have to override _wrap_chunks().''')
 
     def test_properly_formated(self):
 
@@ -241,7 +246,7 @@ bytes() -> empty bytes object''')
             __class__ = property({}.__getitem__, {}.__setitem__)
         class Object(metaclass=Type):
             __slots__ = '__class__'
-        for meth, mtip  in ((Type, default_tip), (Object, default_tip),
+        for meth, mtip  in ((Type, get_spec(type)), (Object, default_tip),
                             (Object(), '')):
             with self.subTest(meth=meth, mtip=mtip):
                 self.assertEqual(get_spec(meth), mtip)
@@ -259,5 +264,100 @@ class Get_entityTest(unittest.TestCase):
         self.assertIs(calltip.get_entity('int'), int)
 
 
+# Test the 9 Calltip methods.
+# open_calltip is about half the code; the others are fairly trivial.
+# The default mocks are what are needed for open_calltip.
+
+class mock_Shell():
+    "Return mock sufficient to pass to hyperparser."
+    def __init__(self, text):
+        text.tag_prevrange = Mock(return_value=None)
+        self.text = text
+        self.prompt_last_line = ">>> "
+        self.indentwidth = 4
+        self.tabwidth = 8
+
+
+class mock_TipWindow:
+    def __init__(self):
+        pass
+
+    def showtip(self, text, parenleft, parenright):
+        self.args = parenleft, parenright
+        self.parenline, self.parencol = map(int, parenleft.split('.'))
+
+
+class WrappedCalltip(calltip.Calltip):
+    def _make_tk_calltip_window(self):
+        return mock_TipWindow()
+
+    def remove_calltip_window(self, event=None):
+        if self.active_calltip:  # Setup to None.
+            self.active_calltip = None
+            self.tips_removed += 1  # Setup to 0.
+
+    def fetch_tip(self, expression):
+        return 'tip'
+
+
+class CalltipTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.text = Text()
+        cls.ct = WrappedCalltip(mock_Shell(cls.text))
+
+    def setUp(self):
+        self.text.delete('1.0', 'end')  # Insert and call
+        self.ct.active_calltip = None
+        # Test .active_calltip, +args
+        self.ct.tips_removed = 0
+
+    def open_close(self, testfunc):
+        # Open-close template with testfunc called in between.
+        opentip = self.ct.open_calltip
+        self.text.insert(1.0, 'f(')
+        opentip(False)
+        self.tip = self.ct.active_calltip
+        testfunc(self)  ###
+        self.text.insert('insert', ')')
+        opentip(False)
+        self.assertIsNone(self.ct.active_calltip, None)
+
+    def test_open_close(self):
+        def args(self):
+            self.assertEqual(self.tip.args, ('1.1', '1.end'))
+        self.open_close(args)
+
+    def test_repeated_force(self):
+        def force(self):
+            for char in 'abc':
+                self.text.insert('insert', 'a')
+                self.ct.open_calltip(True)
+                self.ct.open_calltip(True)
+            self.assertIs(self.ct.active_calltip, self.tip)
+        self.open_close(force)
+
+    def test_repeated_parens(self):
+        def parens(self):
+            for context in "a", "'":
+                with self.subTest(context=context):
+                    self.text.insert('insert', context)
+                    for char in '(()())':
+                        self.text.insert('insert', char)
+                    self.assertIs(self.ct.active_calltip, self.tip)
+            self.text.insert('insert', "'")
+        self.open_close(parens)
+
+    def test_comment_parens(self):
+        def comment(self):
+            self.text.insert('insert', "# ")
+            for char in '(()())':
+                self.text.insert('insert', char)
+            self.assertIs(self.ct.active_calltip, self.tip)
+            self.text.insert('insert', "\n")
+        self.open_close(comment)
+
+
 if __name__ == '__main__':
     unittest.main(verbosity=2)
index aee0c4c..8c9c410 100644 (file)
@@ -76,7 +76,7 @@ class SearchDialogBaseTest(unittest.TestCase):
     def test_make_entry(self):
         equal = self.assertEqual
         self.dialog.row = 0
-        self.dialog.top = self.root
+        self.dialog.frame = Frame(self.root)
         entry, label = self.dialog.make_entry("Test:", 'hello')
         equal(label['text'], 'Test:')
 
@@ -89,7 +89,7 @@ class SearchDialogBaseTest(unittest.TestCase):
         equal(self.dialog.row, 1)
 
     def test_create_entries(self):
-        self.dialog.top = self.root
+        self.dialog.frame = Frame(self.root)
         self.dialog.row = 0
         self.engine.setpat('hello')
         self.dialog.create_entries()
@@ -97,7 +97,7 @@ class SearchDialogBaseTest(unittest.TestCase):
 
     def test_make_frame(self):
         self.dialog.row = 0
-        self.dialog.top = self.root
+        self.dialog.frame = Frame(self.root)
         frame, label = self.dialog.make_frame()
         self.assertEqual(label, '')
         self.assertEqual(str(type(frame)), "<class 'tkinter.ttk.Frame'>")
@@ -108,7 +108,7 @@ class SearchDialogBaseTest(unittest.TestCase):
         self.assertEqual(label['text'], 'testlabel')
 
     def btn_test_setup(self, meth):
-        self.dialog.top = self.root
+        self.dialog.frame = Frame(self.root)
         self.dialog.row = 0
         return meth()
 
@@ -140,13 +140,13 @@ class SearchDialogBaseTest(unittest.TestCase):
                     self.assertEqual(var.get(), state)
 
     def test_make_button(self):
-        self.dialog.top = self.root
-        self.dialog.buttonframe = Frame(self.dialog.top)
+        self.dialog.frame = Frame(self.root)
+        self.dialog.buttonframe = Frame(self.dialog.frame)
         btn = self.dialog.make_button('Test', self.dialog.close)
         self.assertEqual(btn['text'], 'Test')
 
     def test_create_command_buttons(self):
-        self.dialog.top = self.root
+        self.dialog.frame = Frame(self.root)
         self.dialog.create_command_buttons()
         # Look for close button command in buttonframe
         closebuttoncommand = ''
index 3d26d62..f8401ce 100644 (file)
@@ -175,11 +175,13 @@ class SearchEngineTest(unittest.TestCase):
 
         engine.setpat('')
         Equal(engine.getprog(), None)
+        Equal(Mbox.showerror.message,
+              'Error: Empty regular expression')
         engine.setpat('+')
         engine.revar.set(1)
         Equal(engine.getprog(), None)
-        self.assertEqual(Mbox.showerror.message,
-                         'Error: nothing to repeat at position 0\nPattern: +')
+        Equal(Mbox.showerror.message,
+              'Error: nothing to repeat\nPattern: +\nOffset: 0')
 
     def test_report_error(self):
         showerror = Mbox.showerror
index 66ae0f7..adc3028 100755 (executable)
@@ -757,7 +757,7 @@ class ModifiedInterpreter(InteractiveInterpreter):
     def runcode(self, code):
         "Override base class method"
         if self.tkconsole.executing:
-            self.interp.restart_subprocess()
+            self.restart_subprocess()
         self.checklinecache()
         debugger = self.debugger
         try:
@@ -833,7 +833,7 @@ class ModifiedInterpreter(InteractiveInterpreter):
 
 class PyShell(OutputWindow):
 
-    shell_title = "Python " + python_version() + " Shell"
+    shell_title = "IDLE Shell " + python_version()
 
     # Override classes
     ColorDelegator = ModifiedColorDelegator
index a541087..028b0db 100644 (file)
@@ -11,6 +11,7 @@ TODO: Specify command line arguments in a dialog box.
 """
 import os
 import tabnanny
+import time
 import tokenize
 
 import tkinter.messagebox as tkMessageBox
@@ -42,9 +43,7 @@ class ScriptBinding:
         self.root = self.editwin.root
         # cli_args is list of strings that extends sys.argv
         self.cli_args = []
-
-        if macosx.isCocoaTk():
-            self.editwin.text_frame.bind('<<run-module-event-2>>', self._run_module_event)
+        self.perf = 0.0    # Workaround for macOS 11 Uni2; see bpo-42508.
 
     def check_module_event(self, event):
         if isinstance(self.editwin, outwin.OutputWindow):
@@ -107,24 +106,10 @@ class ScriptBinding:
         finally:
             shell.set_warning_stream(saved_stream)
 
-    def run_module_event(self, event):
-        if macosx.isCocoaTk():
-            # Tk-Cocoa in MacOSX is broken until at least
-            # Tk 8.5.9, and without this rather
-            # crude workaround IDLE would hang when a user
-            # tries to run a module using the keyboard shortcut
-            # (the menu item works fine).
-            self.editwin.text_frame.after(200,
-                lambda: self.editwin.text_frame.event_generate(
-                        '<<run-module-event-2>>'))
-            return 'break'
-        else:
-            return self._run_module_event(event)
-
     def run_custom_event(self, event):
-        return self._run_module_event(event, customize=True)
+        return self.run_module_event(event, customize=True)
 
-    def _run_module_event(self, event, *, customize=False):
+    def run_module_event(self, event, *, customize=False):
         """Run the module after setting up the environment.
 
         First check the syntax.  Next get customization.  If OK, make
@@ -133,6 +118,8 @@ class ScriptBinding:
         module being executed and also add that directory to its
         sys.path if not already included.
         """
+        if macosx.isCocoaTk() and (time.perf_counter() - self.perf < .05):
+            return 'break'
         if isinstance(self.editwin, outwin.OutputWindow):
             self.editwin.text.bell()
             return 'break'
@@ -218,6 +205,7 @@ class ScriptBinding:
         # XXX This should really be a function of EditorWindow...
         tkMessageBox.showerror(title, message, parent=self.editwin.text)
         self.editwin.text.focus_set()
+        self.perf = time.perf_counter()
 
 
 if __name__ == "__main__":
index 6fba0b8..fbef87a 100644 (file)
@@ -33,6 +33,7 @@ class SearchDialogBase:
         '''Initialize root, engine, and top attributes.
 
         top (level widget): set in create_widgets() called from open().
+        frame: container for all widgets in dialog.
         text (Text searched): set in open(), only used in subclasses().
         ent (ry): created in make_entry() called from create_entry().
         row (of grid): 0 in create_widgets(), +1 in make_entry/frame().
@@ -83,10 +84,14 @@ class SearchDialogBase:
         top.wm_title(self.title)
         top.wm_iconname(self.icon)
         self.top = top
+        self.frame = Frame(top, padding="5px")
+        self.frame.grid(sticky="nwes")
+        top.grid_columnconfigure(0, weight=100)
+        top.grid_rowconfigure(0, weight=100)
 
         self.row = 0
-        self.top.grid_columnconfigure(0, pad=2, weight=0)
-        self.top.grid_columnconfigure(1, pad=2, minsize=100, weight=100)
+        self.frame.grid_columnconfigure(0, pad=2, weight=0)
+        self.frame.grid_columnconfigure(1, pad=2, minsize=100, weight=100)
 
         self.create_entries()  # row 0 (and maybe 1), cols 0, 1
         self.create_option_buttons()  # next row, cols 0, 1
@@ -99,9 +104,9 @@ class SearchDialogBase:
         entry - gridded labeled Entry for text entry.
         label - Label widget, returned for testing.
         '''
-        label = Label(self.top, text=label_text)
+        label = Label(self.frame, text=label_text)
         label.grid(row=self.row, column=0, sticky="nw")
-        entry = Entry(self.top, textvariable=var, exportselection=0)
+        entry = Entry(self.frame, textvariable=var, exportselection=0)
         entry.grid(row=self.row, column=1, sticky="nwe")
         self.row = self.row + 1
         return entry, label
@@ -117,11 +122,11 @@ class SearchDialogBase:
         label - Label widget, returned for testing.
         '''
         if labeltext:
-            label = Label(self.top, text=labeltext)
+            label = Label(self.frame, text=labeltext)
             label.grid(row=self.row, column=0, sticky="nw")
         else:
             label = ''
-        frame = Frame(self.top)
+        frame = Frame(self.frame)
         frame.grid(row=self.row, column=1, columnspan=1, sticky="nwe")
         self.row = self.row + 1
         return frame, label
@@ -171,7 +176,7 @@ class SearchDialogBase:
 
     def create_command_buttons(self):
         "Place buttons in vertical command frame gridded on right."
-        f = self.buttonframe = Frame(self.top)
+        f = self.buttonframe = Frame(self.frame)
         f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2)
 
         b = self.make_button("Close", self.close)
index 911e7d4..a50038e 100644 (file)
@@ -84,20 +84,17 @@ class SearchEngine:
             flags = flags | re.IGNORECASE
         try:
             prog = re.compile(pat, flags)
-        except re.error as what:
-            args = what.args
-            msg = args[0]
-            col = args[1] if len(args) >= 2 else -1
-            self.report_error(pat, msg, col)
+        except re.error as e:
+            self.report_error(pat, e.msg, e.pos)
             return None
         return prog
 
-    def report_error(self, pat, msg, col=-1):
+    def report_error(self, pat, msg, col=None):
         # Derived class could override this with something fancier
         msg = "Error: " + str(msg)
         if pat:
             msg = msg + "\nPattern: " + str(pat)
-        if col >= 0:
+        if col is not None:
             msg = msg + "\nOffset: " + str(col)
         tkMessageBox.showerror("Regular expression error",
                                msg, master=self.root)
index c071f89..755fafb 100644 (file)
@@ -1,4 +1,4 @@
-from tkinter import Frame, Label
+from tkinter.ttk import Label, Frame
 
 
 class MultiStatusBar(Frame):
@@ -20,7 +20,8 @@ class MultiStatusBar(Frame):
 
 
 def _multistatus_bar(parent):  # htest #
-    from tkinter import Toplevel, Frame, Text, Button
+    from tkinter import Toplevel, Text
+    from tkinter.ttk import Frame, Button
     top = Toplevel(parent)
     x, y = map(int, parent.geometry().split('+')[1:])
     top.geometry("+%d+%d" %(x, y + 175))
index 887a342..18bed90 100644 (file)
@@ -864,7 +864,12 @@ def findsource(object):
         lnum = object.co_firstlineno - 1
         pat = re.compile(r'^(\s*def\s)|(\s*async\s+def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
         while lnum > 0:
-            if pat.match(lines[lnum]): break
+            try:
+                line = lines[lnum]
+            except IndexError:
+                raise OSError('lineno is out of bounds')
+            if pat.match(line):
+                break
             lnum = lnum - 1
         return lines, lnum
     raise OSError('could not find code object')
@@ -926,6 +931,7 @@ class BlockFinder:
         self.indecorator = False
         self.decoratorhasargs = False
         self.last = 1
+        self.body_col0 = None
 
     def tokeneater(self, type, token, srowcol, erowcol, line):
         if not self.started and not self.indecorator:
@@ -957,6 +963,8 @@ class BlockFinder:
         elif self.passline:
             pass
         elif type == tokenize.INDENT:
+            if self.body_col0 is None and self.started:
+                self.body_col0 = erowcol[1]
             self.indent = self.indent + 1
             self.passline = True
         elif type == tokenize.DEDENT:
@@ -966,6 +974,10 @@ class BlockFinder:
             #  not e.g. for "if: else:" or "try: finally:" blocks)
             if self.indent <= 0:
                 raise EndOfBlock
+        elif type == tokenize.COMMENT:
+            if self.body_col0 is not None and srowcol[1] >= self.body_col0:
+                # Include comments if indented at least as much as the block
+                self.last = srowcol[0]
         elif self.indent == 0 and type not in (tokenize.COMMENT, tokenize.NL):
             # any other token on the same indentation level end the previous
             # block as well, except the pseudo-tokens COMMENT and NL.
index 403dc81..7b169a1 100644 (file)
@@ -509,7 +509,7 @@ class Formatter(object):
     responsible for converting a LogRecord to (usually) a string which can
     be interpreted by either a human or an external system. The base Formatter
     allows a formatting string to be specified. If none is supplied, the
-    the style-dependent default value, "%(message)s", "{message}", or
+    style-dependent default value, "%(message)s", "{message}", or
     "${message}", is used.
 
     The Formatter can be initialized with a format string which makes use of
@@ -748,8 +748,8 @@ class Filter(object):
         """
         Determine if the specified record is to be logged.
 
-        Is the specified record to be logged? Returns 0 for no, nonzero for
-        yes. If deemed appropriate, the record may be modified in-place.
+        Returns True if the record should be logged, or False otherwise.
+        If deemed appropriate, the record may be modified in-place.
         """
         if self.nlen == 0:
             return True
index cbac5f1..e63a8b6 100644 (file)
@@ -340,7 +340,9 @@ def whichmodule(obj, name):
     # Protect the iteration by using a list copy of sys.modules against dynamic
     # modules that trigger imports of other modules upon calls to getattr.
     for module_name, module in sys.modules.copy().items():
-        if module_name == '__main__' or module is None:
+        if (module_name == '__main__'
+            or module_name == '__mp_main__'  # bpo-42406
+            or module is None):
             continue
         try:
             if _getattribute(module, name)[0] is obj:
index aff5fe3..2eeebe4 100644 (file)
@@ -173,9 +173,16 @@ class _PlistParser:
         self.parser.StartElementHandler = self.handle_begin_element
         self.parser.EndElementHandler = self.handle_end_element
         self.parser.CharacterDataHandler = self.handle_data
+        self.parser.EntityDeclHandler = self.handle_entity_decl
         self.parser.ParseFile(fileobj)
         return self.root
 
+    def handle_entity_decl(self, entity_name, is_parameter_entity, value, base, system_id, public_id, notation_name):
+        # Reject plist files with entity declarations to avoid XML vulnerabilies in expat.
+        # Regular plist files don't contain those declerations, and Apple's plutil tool does not
+        # accept them either.
+        raise InvalidFileException("XML entity declarations are not supported in plist files")
+
     def handle_begin_element(self, element, attrs):
         self.data = []
         handler = getattr(self, "begin_" + element, None)
@@ -245,7 +252,11 @@ class _PlistParser:
         self.add_object(False)
 
     def end_integer(self):
-        self.add_object(int(self.get_data()))
+        raw = self.get_data()
+        if raw.startswith('0x') or raw.startswith('0X'):
+            self.add_object(int(raw, 16))
+        else:
+            self.add_object(int(raw))
 
     def end_real(self):
         self.add_object(float(self.get_data()))
@@ -466,7 +477,7 @@ class _BinaryPlistParser:
             return self._read_object(top_object)
 
         except (OSError, IndexError, struct.error, OverflowError,
-                UnicodeDecodeError):
+                ValueError):
             raise InvalidFileException()
 
     def _get_size(self, tokenL):
@@ -482,7 +493,7 @@ class _BinaryPlistParser:
     def _read_ints(self, n, size):
         data = self._fp.read(size * n)
         if size in _BINARY_FORMAT:
-            return struct.unpack('>' + _BINARY_FORMAT[size] * n, data)
+            return struct.unpack(f'>{n}{_BINARY_FORMAT[size]}', data)
         else:
             if not size or len(data) != size * n:
                 raise InvalidFileException()
@@ -542,14 +553,22 @@ class _BinaryPlistParser:
         elif tokenH == 0x40:  # data
             s = self._get_size(tokenL)
             result = self._fp.read(s)
+            if len(result) != s:
+                raise InvalidFileException()
 
         elif tokenH == 0x50:  # ascii string
             s = self._get_size(tokenL)
-            result =  self._fp.read(s).decode('ascii')
+            data = self._fp.read(s)
+            if len(data) != s:
+                raise InvalidFileException()
+            result = data.decode('ascii')
 
         elif tokenH == 0x60:  # unicode string
-            s = self._get_size(tokenL)
-            result = self._fp.read(s * 2).decode('utf-16be')
+            s = self._get_size(tokenL) * 2
+            data = self._fp.read(s)
+            if len(data) != s:
+                raise InvalidFileException()
+            result = data.decode('utf-16be')
 
         elif tokenH == 0x80:  # UID
             # used by Key-Archiver plist files
@@ -574,9 +593,11 @@ class _BinaryPlistParser:
             obj_refs = self._read_refs(s)
             result = self._dict_type()
             self._objects[ref] = result
-            for k, o in zip(key_refs, obj_refs):
-                result[self._read_object(k)] = self._read_object(o)
-
+            try:
+                for k, o in zip(key_refs, obj_refs):
+                    result[self._read_object(k)] = self._read_object(o)
+            except TypeError:
+                raise InvalidFileException()
         else:
             raise InvalidFileException()
 
@@ -590,7 +611,7 @@ def _count_to_size(count):
     elif count < 1 << 16:
         return 2
 
-    elif count << 1 << 32:
+    elif count < 1 << 32:
         return 4
 
     else:
index aad458d..5cb017e 100755 (executable)
@@ -571,6 +571,11 @@ def main():
     (options, args) = parser.parse_args()
     sys.argv[:] = args
 
+    # The script that we're profiling may chdir, so capture the absolute path
+    # to the output file at startup.
+    if options.outfile is not None:
+        options.outfile = os.path.abspath(options.outfile)
+
     if len(args) > 0:
         if options.module:
             import runpy
index e781b91..0f93ae0 100644 (file)
@@ -45,9 +45,9 @@ class SortKey(str, Enum):
     TIME = 'time', 'tottime'
 
     def __new__(cls, *values):
-        obj = str.__new__(cls)
-
-        obj._value_ = values[0]
+        value = values[0]
+        obj = str.__new__(cls, value)
+        obj._value_ = value
         for other_value in values[1:]:
             cls._value2member_map_[other_value] = obj
         obj._all_values = values
index 40b4579..d8dd8c5 100644 (file)
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Autogenerated by Sphinx on Sun Oct  4 19:26:28 2020
+# Autogenerated by Sphinx on Mon Dec  7 15:00:07 2020
 topics = {'assert': 'The "assert" statement\n'
            '**********************\n'
            '\n'
@@ -700,6 +700,11 @@ topics = {'assert': 'The "assert" statement\n'
                      'syntax or\n'
                      '     built-in functions. See Special method lookup.\n'
                      '\n'
+                     '   For certain sensitive attribute accesses, raises an '
+                     'auditing event\n'
+                     '   "object.__getattr__" with arguments "obj" and '
+                     '"name".\n'
+                     '\n'
                      'object.__setattr__(self, name, value)\n'
                      '\n'
                      '   Called when an attribute assignment is attempted.  '
@@ -716,6 +721,11 @@ topics = {'assert': 'The "assert" statement\n'
                      'for example,\n'
                      '   "object.__setattr__(self, name, value)".\n'
                      '\n'
+                     '   For certain sensitive attribute assignments, raises '
+                     'an auditing\n'
+                     '   event "object.__setattr__" with arguments "obj", '
+                     '"name", "value".\n'
+                     '\n'
                      'object.__delattr__(self, name)\n'
                      '\n'
                      '   Like "__setattr__()" but for attribute deletion '
@@ -724,6 +734,11 @@ topics = {'assert': 'The "assert" statement\n'
                      'obj.name" is\n'
                      '   meaningful for the object.\n'
                      '\n'
+                     '   For certain sensitive attribute deletions, raises an '
+                     'auditing event\n'
+                     '   "object.__delattr__" with arguments "obj" and '
+                     '"name".\n'
+                     '\n'
                      'object.__dir__(self)\n'
                      '\n'
                      '   Called when "dir()" is called on the object. A '
@@ -1464,8 +1479,8 @@ topics = {'assert': 'The "assert" statement\n'
                    '\n'
                    '   Called when the instance is “called” as a function; if '
                    'this method\n'
-                   '   is defined, "x(arg1, arg2, ...)" is a shorthand for\n'
-                   '   "x.__call__(arg1, arg2, ...)".\n',
+                   '   is defined, "x(arg1, arg2, ...)" roughly translates to\n'
+                   '   "type(x).__call__(x, arg1, ...)".\n',
  'calls': 'Calls\n'
           '*****\n'
           '\n'
@@ -3461,16 +3476,21 @@ topics = {'assert': 'The "assert" statement\n'
                   '   on the value to determine if the result is true or '
                   'false.\n'
                   '\n'
-                  '   By default, "__ne__()" delegates to "__eq__()" and '
-                  'inverts the\n'
-                  '   result unless it is "NotImplemented".  There are no '
-                  'other implied\n'
-                  '   relationships among the comparison operators, for '
-                  'example, the\n'
-                  '   truth of "(x<y or x==y)" does not imply "x<=y". To '
-                  'automatically\n'
-                  '   generate ordering operations from a single root '
-                  'operation, see\n'
+                  '   By default, "object" implements "__eq__()" by using '
+                  '"is", returning\n'
+                  '   "NotImplemented" in the case of a false comparison: '
+                  '"True if x is y\n'
+                  '   else NotImplemented". For "__ne__()", by default it '
+                  'delegates to\n'
+                  '   "__eq__()" and inverts the result unless it is '
+                  '"NotImplemented".\n'
+                  '   There are no other implied relationships among the '
+                  'comparison\n'
+                  '   operators or default implementations; for example, the '
+                  'truth of\n'
+                  '   "(x<y or x==y)" does not imply "x<=y". To automatically '
+                  'generate\n'
+                  '   ordering operations from a single root operation, see\n'
                   '   "functools.total_ordering()".\n'
                   '\n'
                   '   See the paragraph on "__hash__()" for some important '
@@ -5287,24 +5307,23 @@ topics = {'assert': 'The "assert" statement\n'
                   'for the\n'
                   'conversion.  The alternate form is defined differently for '
                   'different\n'
-                  'types.  This option is only valid for integer, float, '
-                  'complex and\n'
-                  'Decimal types. For integers, when binary, octal, or '
-                  'hexadecimal output\n'
-                  'is used, this option adds the prefix respective "\'0b\'", '
-                  '"\'0o\'", or\n'
-                  '"\'0x\'" to the output value. For floats, complex and '
-                  'Decimal the\n'
-                  'alternate form causes the result of the conversion to '
-                  'always contain a\n'
-                  'decimal-point character, even if no digits follow it. '
-                  'Normally, a\n'
-                  'decimal-point character appears in the result of these '
-                  'conversions\n'
-                  'only if a digit follows it. In addition, for "\'g\'" and '
-                  '"\'G\'"\n'
-                  'conversions, trailing zeros are not removed from the '
-                  'result.\n'
+                  'types.  This option is only valid for integer, float and '
+                  'complex\n'
+                  'types. For integers, when binary, octal, or hexadecimal '
+                  'output is\n'
+                  'used, this option adds the prefix respective "\'0b\'", '
+                  '"\'0o\'", or "\'0x\'"\n'
+                  'to the output value. For float and complex the alternate '
+                  'form causes\n'
+                  'the result of the conversion to always contain a '
+                  'decimal-point\n'
+                  'character, even if no digits follow it. Normally, a '
+                  'decimal-point\n'
+                  'character appears in the result of these conversions only '
+                  'if a digit\n'
+                  'follows it. In addition, for "\'g\'" and "\'G\'" '
+                  'conversions, trailing\n'
+                  'zeros are not removed from the result.\n'
                   '\n'
                   'The "\',\'" option signals the use of a comma for a '
                   'thousands separator.\n'
@@ -5442,9 +5461,8 @@ topics = {'assert': 'The "assert" statement\n'
                   'the integer\n'
                   'to a floating point number before formatting.\n'
                   '\n'
-                  'The available presentation types for floating point and '
-                  'decimal values\n'
-                  'are:\n'
+                  'The available presentation types for "float" and "Decimal" '
+                  'values are:\n'
                   '\n'
                   '   '
                   '+-----------+------------------------------------------------------------+\n'
@@ -5453,24 +5471,50 @@ topics = {'assert': 'The "assert" statement\n'
                   '|\n'
                   '   '
                   '|===========|============================================================|\n'
-                  '   | "\'e\'"     | Exponent notation. Prints the number in '
-                  'scientific         |\n'
-                  '   |           | notation using the letter ‘e’ to indicate '
-                  'the exponent.    |\n'
-                  '   |           | The default precision is '
-                  '"6".                              |\n'
+                  '   | "\'e\'"     | Scientific notation. For a given '
+                  'precision "p", formats    |\n'
+                  '   |           | the number in scientific notation with the '
+                  'letter ‘e’      |\n'
+                  '   |           | separating the coefficient from the '
+                  'exponent. The          |\n'
+                  '   |           | coefficient has one digit before and "p" '
+                  'digits after the  |\n'
+                  '   |           | decimal point, for a total of "p + 1" '
+                  'significant digits.  |\n'
+                  '   |           | With no precision given, uses a precision '
+                  'of "6" digits    |\n'
+                  '   |           | after the decimal point for "float", and '
+                  'shows all         |\n'
+                  '   |           | coefficient digits for "Decimal". If no '
+                  'digits follow the  |\n'
+                  '   |           | decimal point, the decimal point is also '
+                  'removed unless    |\n'
+                  '   |           | the "#" option is '
+                  'used.                                    |\n'
                   '   '
                   '+-----------+------------------------------------------------------------+\n'
-                  '   | "\'E\'"     | Exponent notation. Same as "\'e\'" '
-                  'except it uses an upper   |\n'
+                  '   | "\'E\'"     | Scientific notation. Same as "\'e\'" '
+                  'except it uses an upper |\n'
                   '   |           | case ‘E’ as the separator '
                   'character.                       |\n'
                   '   '
                   '+-----------+------------------------------------------------------------+\n'
-                  '   | "\'f\'"     | Fixed-point notation. Displays the '
-                  'number as a fixed-point |\n'
-                  '   |           | number. The default precision is '
-                  '"6".                      |\n'
+                  '   | "\'f\'"     | Fixed-point notation. For a given '
+                  'precision "p", formats   |\n'
+                  '   |           | the number as a decimal number with '
+                  'exactly "p" digits     |\n'
+                  '   |           | following the decimal point. With no '
+                  'precision given, uses |\n'
+                  '   |           | a precision of "6" digits after the '
+                  'decimal point for      |\n'
+                  '   |           | "float", and uses a precision large enough '
+                  'to show all     |\n'
+                  '   |           | coefficient digits for "Decimal". If no '
+                  'digits follow the  |\n'
+                  '   |           | decimal point, the decimal point is also '
+                  'removed unless    |\n'
+                  '   |           | the "#" option is '
+                  'used.                                    |\n'
                   '   '
                   '+-----------+------------------------------------------------------------+\n'
                   '   | "\'F\'"     | Fixed-point notation. Same as "\'f\'", '
@@ -5516,9 +5560,14 @@ topics = {'assert': 'The "assert" statement\n'
                   '   |           | regardless of the precision.  A precision '
                   'of "0" is        |\n'
                   '   |           | treated as equivalent to a precision of '
-                  '"1". The default   |\n'
-                  '   |           | precision is '
-                  '"6".                                          |\n'
+                  '"1". With no       |\n'
+                  '   |           | precision given, uses a precision of "6" '
+                  'significant       |\n'
+                  '   |           | digits for "float", and shows all '
+                  'coefficient digits for   |\n'
+                  '   |           | '
+                  '"Decimal".                                                 '
+                  '|\n'
                   '   '
                   '+-----------+------------------------------------------------------------+\n'
                   '   | "\'G\'"     | General format. Same as "\'g\'" except '
@@ -6395,8 +6444,8 @@ topics = {'assert': 'The "assert" statement\n'
            '\n'
            '* other future statements.\n'
            '\n'
-           'The only feature in Python 3.7 that requires using the future\n'
-           'statement is "annotations".\n'
+           'The only feature that requires using the future statement is\n'
+           '"annotations" (see **PEP 563**).\n'
            '\n'
            'All historical features enabled by the future statement are still\n'
            'recognized by Python 3.  The list includes "absolute_import",\n'
@@ -6928,13 +6977,14 @@ topics = {'assert': 'The "assert" statement\n'
                   '\n'
                   '     If the right operand’s type is a subclass of the left '
                   'operand’s\n'
-                  '     type and that subclass provides the reflected method '
-                  'for the\n'
-                  '     operation, this method will be called before the left '
-                  'operand’s\n'
-                  '     non-reflected method.  This behavior allows subclasses '
-                  'to\n'
-                  '     override their ancestors’ operations.\n'
+                  '     type and that subclass provides a different '
+                  'implementation of the\n'
+                  '     reflected method for the operation, this method will '
+                  'be called\n'
+                  '     before the left operand’s non-reflected method. This '
+                  'behavior\n'
+                  '     allows subclasses to override their ancestors’ '
+                  'operations.\n'
                   '\n'
                   'object.__iadd__(self, other)\n'
                   'object.__isub__(self, other)\n'
@@ -8251,16 +8301,21 @@ topics = {'assert': 'The "assert" statement\n'
                  '   on the value to determine if the result is true or '
                  'false.\n'
                  '\n'
-                 '   By default, "__ne__()" delegates to "__eq__()" and '
-                 'inverts the\n'
-                 '   result unless it is "NotImplemented".  There are no other '
-                 'implied\n'
-                 '   relationships among the comparison operators, for '
-                 'example, the\n'
-                 '   truth of "(x<y or x==y)" does not imply "x<=y". To '
-                 'automatically\n'
-                 '   generate ordering operations from a single root '
-                 'operation, see\n'
+                 '   By default, "object" implements "__eq__()" by using "is", '
+                 'returning\n'
+                 '   "NotImplemented" in the case of a false comparison: "True '
+                 'if x is y\n'
+                 '   else NotImplemented". For "__ne__()", by default it '
+                 'delegates to\n'
+                 '   "__eq__()" and inverts the result unless it is '
+                 '"NotImplemented".\n'
+                 '   There are no other implied relationships among the '
+                 'comparison\n'
+                 '   operators or default implementations; for example, the '
+                 'truth of\n'
+                 '   "(x<y or x==y)" does not imply "x<=y". To automatically '
+                 'generate\n'
+                 '   ordering operations from a single root operation, see\n'
                  '   "functools.total_ordering()".\n'
                  '\n'
                  '   See the paragraph on "__hash__()" for some important '
@@ -8490,6 +8545,10 @@ topics = {'assert': 'The "assert" statement\n'
                  'syntax or\n'
                  '     built-in functions. See Special method lookup.\n'
                  '\n'
+                 '   For certain sensitive attribute accesses, raises an '
+                 'auditing event\n'
+                 '   "object.__getattr__" with arguments "obj" and "name".\n'
+                 '\n'
                  'object.__setattr__(self, name, value)\n'
                  '\n'
                  '   Called when an attribute assignment is attempted.  This '
@@ -8506,6 +8565,11 @@ topics = {'assert': 'The "assert" statement\n'
                  'example,\n'
                  '   "object.__setattr__(self, name, value)".\n'
                  '\n'
+                 '   For certain sensitive attribute assignments, raises an '
+                 'auditing\n'
+                 '   event "object.__setattr__" with arguments "obj", "name", '
+                 '"value".\n'
+                 '\n'
                  'object.__delattr__(self, name)\n'
                  '\n'
                  '   Like "__setattr__()" but for attribute deletion instead '
@@ -8514,6 +8578,10 @@ topics = {'assert': 'The "assert" statement\n'
                  'obj.name" is\n'
                  '   meaningful for the object.\n'
                  '\n'
+                 '   For certain sensitive attribute deletions, raises an '
+                 'auditing event\n'
+                 '   "object.__delattr__" with arguments "obj" and "name".\n'
+                 '\n'
                  'object.__dir__(self)\n'
                  '\n'
                  '   Called when "dir()" is called on the object. A sequence '
@@ -9307,8 +9375,8 @@ topics = {'assert': 'The "assert" statement\n'
                  '\n'
                  '   Called when the instance is “called” as a function; if '
                  'this method\n'
-                 '   is defined, "x(arg1, arg2, ...)" is a shorthand for\n'
-                 '   "x.__call__(arg1, arg2, ...)".\n'
+                 '   is defined, "x(arg1, arg2, ...)" roughly translates to\n'
+                 '   "type(x).__call__(x, arg1, ...)".\n'
                  '\n'
                  '\n'
                  'Emulating container types\n'
@@ -9641,13 +9709,14 @@ topics = {'assert': 'The "assert" statement\n'
                  '\n'
                  '     If the right operand’s type is a subclass of the left '
                  'operand’s\n'
-                 '     type and that subclass provides the reflected method '
-                 'for the\n'
-                 '     operation, this method will be called before the left '
-                 'operand’s\n'
-                 '     non-reflected method.  This behavior allows subclasses '
-                 'to\n'
-                 '     override their ancestors’ operations.\n'
+                 '     type and that subclass provides a different '
+                 'implementation of the\n'
+                 '     reflected method for the operation, this method will be '
+                 'called\n'
+                 '     before the left operand’s non-reflected method. This '
+                 'behavior\n'
+                 '     allows subclasses to override their ancestors’ '
+                 'operations.\n'
                  '\n'
                  'object.__iadd__(self, other)\n'
                  'object.__isub__(self, other)\n'
@@ -11072,9 +11141,10 @@ topics = {'assert': 'The "assert" statement\n'
  'subscriptions': 'Subscriptions\n'
                   '*************\n'
                   '\n'
-                  'A subscription selects an item of a sequence (string, tuple '
-                  'or list)\n'
-                  'or mapping (dictionary) object:\n'
+                  'Subscription of a sequence (string, tuple or list) or '
+                  'mapping\n'
+                  '(dictionary) object usually selects an item from the '
+                  'collection:\n'
                   '\n'
                   '   subscription ::= primary "[" expression_list "]"\n'
                   '\n'
@@ -11125,7 +11195,13 @@ topics = {'assert': 'The "assert" statement\n'
                   '\n'
                   'A string’s items are characters.  A character is not a '
                   'separate data\n'
-                  'type but a string of exactly one character.\n',
+                  'type but a string of exactly one character.\n'
+                  '\n'
+                  'Subscription of certain *classes* or *types* creates a '
+                  'generic alias.\n'
+                  'In this case, user-defined classes can support subscription '
+                  'by\n'
+                  'providing a "__class_getitem__()" classmethod.\n',
  'truth': 'Truth Value Testing\n'
           '*******************\n'
           '\n'
@@ -11371,6 +11447,27 @@ topics = {'assert': 'The "assert" statement\n'
           'representation\n'
           '   in computers.\n'
           '\n'
+          '   The string representations of the numeric classes, computed by\n'
+          '   "__repr__()" and "__str__()", have the following properties:\n'
+          '\n'
+          '   * They are valid numeric literals which, when passed to their '
+          'class\n'
+          '     constructor, produce an object having the value of the '
+          'original\n'
+          '     numeric.\n'
+          '\n'
+          '   * The representation is in base 10, when possible.\n'
+          '\n'
+          '   * Leading zeros, possibly excepting a single zero before a '
+          'decimal\n'
+          '     point, are not shown.\n'
+          '\n'
+          '   * Trailing zeros, possibly excepting a single zero after a '
+          'decimal\n'
+          '     point, are not shown.\n'
+          '\n'
+          '   * A sign is shown only when the number is negative.\n'
+          '\n'
           '   Python distinguishes between integers, floating point numbers, '
           'and\n'
           '   complex numbers:\n'
@@ -12422,6 +12519,21 @@ topics = {'assert': 'The "assert" statement\n'
                  'positional\n'
                  '   argument and a possibly empty set of keyword arguments.\n'
                  '\n'
+                 '   Dictionaries can be created by several means:\n'
+                 '\n'
+                 '   * Use a comma-separated list of "key: value" pairs within '
+                 'braces:\n'
+                 '     "{\'jack\': 4098, \'sjoerd\': 4127}" or "{4098: '
+                 "'jack', 4127:\n"
+                 '     \'sjoerd\'}"\n'
+                 '\n'
+                 '   * Use a dict comprehension: "{}", "{x: x ** 2 for x in '
+                 'range(10)}"\n'
+                 '\n'
+                 '   * Use the type constructor: "dict()", "dict([(\'foo\', '
+                 "100), ('bar',\n"
+                 '     200)])", "dict(foo=100, bar=200)"\n'
+                 '\n'
                  '   If no positional argument is given, an empty dictionary '
                  'is created.\n'
                  '   If a positional argument is given and it is a mapping '
index a4ce2c0..f0e833d 100644 (file)
@@ -53,6 +53,9 @@ COPY_BUFSIZE = 1024 * 1024 if _WINDOWS else 64 * 1024
 _USE_CP_SENDFILE = hasattr(os, "sendfile") and sys.platform.startswith("linux")
 _HAS_FCOPYFILE = posix and hasattr(posix, "_fcopyfile")  # macOS
 
+# CMD defaults in Windows 10
+_WIN_DEFAULT_PATHEXT = ".COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC"
+
 __all__ = ["copyfileobj", "copyfile", "copymode", "copystat", "copy", "copy2",
            "copytree", "move", "rmtree", "Error", "SpecialFileError",
            "ExecError", "make_archive", "get_archive_formats",
@@ -708,7 +711,7 @@ def rmtree(path, ignore_errors=False, onerror=None):
         try:
             fd = os.open(path, os.O_RDONLY)
         except Exception:
-            onerror(os.lstat, path, sys.exc_info())
+            onerror(os.open, path, sys.exc_info())
             return
         try:
             if os.path.samestat(orig_st, os.fstat(fd)):
@@ -1415,7 +1418,9 @@ def which(cmd, mode=os.F_OK | os.X_OK, path=None):
             path.insert(0, curdir)
 
         # PATHEXT is necessary to check on Windows.
-        pathext = os.environ.get("PATHEXT", "").split(os.pathsep)
+        pathext_source = os.getenv("PATHEXT") or _WIN_DEFAULT_PATHEXT
+        pathext = [ext for ext in pathext_source.split(os.pathsep) if ext]
+
         if use_bytes:
             pathext = [os.fsencode(ext) for ext in pathext]
         # See if the given file matches any of the expected path extensions.
index 13600c2..f1d829a 100644 (file)
@@ -2061,7 +2061,11 @@ class Popen(object):
             # The race condition can still happen if the race condition
             # described above happens between the returncode test
             # and the kill() call.
-            os.kill(self.pid, sig)
+            try:
+                os.kill(self.pid, sig)
+            except ProcessLookupError:
+                # Supress the race condition error; bpo-40550.
+                pass
 
         def terminate(self):
             """Terminate the process with SIGTERM
index a711676..521540f 100644 (file)
@@ -34,7 +34,7 @@ class SymbolTableFactory:
 _newSymbolTable = SymbolTableFactory()
 
 
-class SymbolTable(object):
+class SymbolTable:
 
     def __init__(self, raw_table, filename):
         self._table = raw_table
@@ -47,7 +47,7 @@ class SymbolTable(object):
         else:
             kind = "%s " % self.__class__.__name__
 
-        if self._table.name == "global":
+        if self._table.name == "top":
             return "<{0}SymbolTable for module {1}>".format(kind, self._filename)
         else:
             return "<{0}SymbolTable for {1} in {2}>".format(kind,
@@ -90,7 +90,9 @@ class SymbolTable(object):
         if sym is None:
             flags = self._table.symbols[name]
             namespaces = self.__check_children(name)
-            sym = self._symbols[name] = Symbol(name, flags, namespaces)
+            module_scope = (self._table.name == "top")
+            sym = self._symbols[name] = Symbol(name, flags, namespaces,
+                                               module_scope=module_scope)
         return sym
 
     def get_symbols(self):
@@ -163,13 +165,14 @@ class Class(SymbolTable):
         return self.__methods
 
 
-class Symbol(object):
+class Symbol:
 
-    def __init__(self, name, flags, namespaces=None):
+    def __init__(self, name, flags, namespaces=None, *, module_scope=False):
         self.__name = name
         self.__flags = flags
         self.__scope = (flags >> SCOPE_OFF) & SCOPE_MASK # like PyST_GetScope()
         self.__namespaces = namespaces or ()
+        self.__module_scope = module_scope
 
     def __repr__(self):
         return "<symbol {0!r}>".format(self.__name)
@@ -184,7 +187,10 @@ class Symbol(object):
         return bool(self.__flags & DEF_PARAM)
 
     def is_global(self):
-        return bool(self.__scope in (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT))
+        """Return *True* if the sysmbol is global.
+        """
+        return bool(self.__scope in (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT)
+                    or (self.__module_scope and self.__flags & DEF_BOUND))
 
     def is_nonlocal(self):
         return bool(self.__flags & DEF_NONLOCAL)
@@ -193,7 +199,10 @@ class Symbol(object):
         return bool(self.__scope == GLOBAL_EXPLICIT)
 
     def is_local(self):
-        return bool(self.__scope in (LOCAL, CELL))
+        """Return *True* if the symbol is local.
+        """
+        return bool(self.__scope in (LOCAL, CELL)
+                    or (self.__module_scope and self.__flags & DEF_BOUND))
 
     def is_annotated(self):
         return bool(self.__flags & DEF_ANNOT)
index 6769066..1d15612 100755 (executable)
@@ -420,6 +420,8 @@ class _Stream:
         self.__write(b"\037\213\010\010" + timestamp + b"\002\377")
         if self.name.endswith(".gz"):
             self.name = self.name[:-3]
+        # Honor "directory components removed" from RFC1952
+        self.name = os.path.basename(self.name)
         # RFC1952 says we must use ISO-8859-1 for the FNAME field.
         self.__write(self.name.encode("iso-8859-1", "replace") + NUL)
 
@@ -2091,9 +2093,10 @@ class TarFile(object):
 
     def extractfile(self, member):
         """Extract a member from the archive as a file object. `member' may be
-           a filename or a TarInfo object. If `member' is a regular file or a
-           link, an io.BufferedReader object is returned. Otherwise, None is
-           returned.
+           a filename or a TarInfo object. If `member' is a regular file or
+           a link, an io.BufferedReader object is returned. For all other
+           existing members, None is returned. If `member' does not appear
+           in the archive, KeyError is raised.
         """
         self._check("r")
 
@@ -2234,6 +2237,9 @@ class TarFile(object):
         try:
             # For systems that support symbolic and hard links.
             if tarinfo.issym():
+                if os.path.lexists(targetpath):
+                    # Avoid FileExistsError on following os.symlink.
+                    os.unlink(targetpath)
                 os.symlink(tarinfo.linkname, targetpath)
             else:
                 # See extract().
index a9741d6..b37ef91 100644 (file)
@@ -1781,6 +1781,7 @@ class TestDate(HarmlessMixedComparison, unittest.TestCase):
             green = pickler.dumps(orig, proto)
             derived = unpickler.loads(green)
             self.assertEqual(orig, derived)
+            self.assertTrue(isinstance(derived, SubclassDate))
 
     def test_backdoor_resistance(self):
         # For fast unpickling, the constructor accepts a pickle byte string.
@@ -2308,6 +2309,7 @@ class TestDateTime(TestDate):
             green = pickler.dumps(orig, proto)
             derived = unpickler.loads(green)
             self.assertEqual(orig, derived)
+            self.assertTrue(isinstance(derived, SubclassDatetime))
 
     def test_compat_unpickle(self):
         tests = [
@@ -3357,6 +3359,7 @@ class TestTime(HarmlessMixedComparison, unittest.TestCase):
             green = pickler.dumps(orig, proto)
             derived = unpickler.loads(green)
             self.assertEqual(orig, derived)
+            self.assertTrue(isinstance(derived, SubclassTime))
 
     def test_compat_unpickle(self):
         tests = [
index 96a0257..e1287a3 100644 (file)
@@ -91,3 +91,25 @@ class Callable:
 
 custom_method = Callable().as_method_of(42)
 del Callable
+
+# line 95
+class WhichComments:
+  # line 97
+    # before f
+    def f(self):
+      # line 100
+        # start f
+        return 1
+        # line 103
+        # end f
+       # line 105
+    # after f
+
+    # before asyncf - line 108
+    async def asyncf(self):
+        # start asyncf
+        return 2
+        # end asyncf
+       # after asyncf - line 113
+    # end of WhichComments - line 114
+  # after WhichComments - line 115
index cca8af6..f76c015 100644 (file)
@@ -305,29 +305,23 @@ class TestBase_Mapping(unittest.TestCase):
             self._test_mapping_file_plain()
 
     def _test_mapping_file_plain(self):
-        unichrs = lambda s: ''.join(map(chr, map(eval, s.split('+'))))
+        def unichrs(s):
+            return ''.join(chr(int(x, 16)) for x in s.split('+'))
+
         urt_wa = {}
 
         with self.open_mapping_file() as f:
             for line in f:
                 if not line:
                     break
-                data = line.split('#')[0].strip().split()
+                data = line.split('#')[0].split()
                 if len(data) != 2:
                     continue
 
-                csetval = eval(data[0])
-                if csetval <= 0x7F:
-                    csetch = bytes([csetval & 0xff])
-                elif csetval >= 0x1000000:
-                    csetch = bytes([(csetval >> 24), ((csetval >> 16) & 0xff),
-                                    ((csetval >> 8) & 0xff), (csetval & 0xff)])
-                elif csetval >= 0x10000:
-                    csetch = bytes([(csetval >> 16), ((csetval >> 8) & 0xff),
-                                    (csetval & 0xff)])
-                elif csetval >= 0x100:
-                    csetch = bytes([(csetval >> 8), (csetval & 0xff)])
-                else:
+                if data[0][:2] != '0x':
+                    self.fail(f"Invalid line: {line!r}")
+                csetch = bytes.fromhex(data[0][2:])
+                if len(csetch) == 1 and 0x80 <= csetch[0]:
                     continue
 
                 unich = unichrs(data[1])
index 94d42c4..3d54617 100644 (file)
@@ -1965,6 +1965,17 @@ class AbstractPickleTests(unittest.TestCase):
                 self.assertEqual(B(x), B(y), detail)
                 self.assertEqual(x.__dict__, y.__dict__, detail)
 
+    def test_newobj_overridden_new(self):
+        # Test that Python class with C implemented __new__ is pickleable
+        for proto in protocols:
+            x = MyIntWithNew2(1)
+            x.foo = 42
+            s = self.dumps(x, proto)
+            y = self.loads(s)
+            self.assertIs(type(y), MyIntWithNew2)
+            self.assertEqual(int(y), 1)
+            self.assertEqual(y.foo, 42)
+
     def test_newobj_not_class(self):
         # Issue 24552
         global SimpleNewObj
@@ -3085,6 +3096,13 @@ myclasses = [MyInt, MyFloat,
              MyStr, MyUnicode,
              MyTuple, MyList, MyDict, MySet, MyFrozenSet]
 
+class MyIntWithNew(int):
+    def __new__(cls, value):
+        raise AssertionError
+
+class MyIntWithNew2(MyIntWithNew):
+    __new__ = int.__new__
+
 
 class SlotList(MyList):
     __slots__ = ["foo"]
index cda0ee9..59a00ba 100644 (file)
@@ -72,6 +72,10 @@ known_numerics = {
     'ps_AF': ('\u066b', '\u066c'),
 }
 
+if sys.platform == 'win32':
+    # ps_AF doesn't work on Windows: see bpo-38324 (msg361830)
+    del known_numerics['ps_AF']
+
 class _LocaleTests(unittest.TestCase):
 
     def setUp(self):
index 7bc9c9a..fa6c49d 100644 (file)
@@ -293,11 +293,8 @@ class EventLoopTestsMixin:
             self.loop.stop()
 
         self.loop.call_later(0.1, callback, 'hello world')
-        t0 = time.monotonic()
         self.loop.run_forever()
-        t1 = time.monotonic()
         self.assertEqual(results, ['hello world'])
-        self.assertTrue(0.08 <= t1-t0 <= 0.8, t1-t0)
 
     def test_call_soon(self):
         results = []
@@ -1075,6 +1072,7 @@ class EventLoopTestsMixin:
                                                ssl=sslcontext_client,
                                                server_hostname='localhost')
         client, pr = self.loop.run_until_complete(f_c)
+        self.loop.run_until_complete(proto.connected)
 
         # close connection
         proto.transport.close()
@@ -1100,6 +1098,7 @@ class EventLoopTestsMixin:
                                           ssl=sslcontext_client,
                                           server_hostname='localhost')
         client, pr = self.loop.run_until_complete(f_c)
+        self.loop.run_until_complete(proto.connected)
 
         # extra info is available
         self.check_ssl_extra_info(client, peername=(host, port),
diff --git a/Lib/test/test_asyncio/test_futures2.py b/Lib/test/test_asyncio/test_futures2.py
new file mode 100644 (file)
index 0000000..13dbc70
--- /dev/null
@@ -0,0 +1,18 @@
+# IsolatedAsyncioTestCase based tests
+import asyncio
+import unittest
+
+
+class FutureTests(unittest.IsolatedAsyncioTestCase):
+    async def test_recursive_repr_for_pending_tasks(self):
+        # The call crashes if the guard for recursive call
+        # in base_futures:_future_repr_info is absent
+        # See Also: https://bugs.python.org/issue42183
+
+        async def func():
+            return asyncio.all_tasks()
+
+        # The repr() call should not raise RecursiveError at first.
+        # The check for returned string is not very reliable but
+        # exact comparison for the whole string is even weaker.
+        self.assertIn('...', repr(await asyncio.wait_for(func(), timeout=10)))
index 1e9d115..71de82d 100644 (file)
@@ -452,12 +452,14 @@ class StreamTests(test_utils.TestCase):
 
     def test_readuntil_eof(self):
         stream = asyncio.StreamReader(loop=self.loop)
-        stream.feed_data(b'some dataAA')
+        data = b'some dataAA'
+        stream.feed_data(data)
         stream.feed_eof()
 
-        with self.assertRaises(asyncio.IncompleteReadError) as cm:
+        with self.assertRaisesRegex(asyncio.IncompleteReadError,
+                                    'undefined expected bytes') as cm:
             self.loop.run_until_complete(stream.readuntil(b'AAA'))
-        self.assertEqual(cm.exception.partial, b'some dataAA')
+        self.assertEqual(cm.exception.partial, data)
         self.assertIsNone(cm.exception.expected)
         self.assertEqual(b'', stream._buffer)
 
index 74fc1e4..01f62b7 100644 (file)
@@ -1548,6 +1548,30 @@ class BaseTaskTests:
         loop.advance_time(10)
         loop.run_until_complete(asyncio.wait([a, b]))
 
+    def test_wait_with_iterator_of_tasks(self):
+
+        def gen():
+            when = yield
+            self.assertAlmostEqual(0.1, when)
+            when = yield 0
+            self.assertAlmostEqual(0.15, when)
+            yield 0.15
+
+        loop = self.new_test_loop(gen)
+
+        a = self.new_task(loop, asyncio.sleep(0.1))
+        b = self.new_task(loop, asyncio.sleep(0.15))
+
+        async def foo():
+            done, pending = await asyncio.wait(iter([b, a]))
+            self.assertEqual(done, set([a, b]))
+            self.assertEqual(pending, set())
+            return 42
+
+        res = loop.run_until_complete(self.new_task(loop, foo()))
+        self.assertEqual(res, 42)
+        self.assertAlmostEqual(0.15, loop.time())
+
     def test_as_completed(self):
 
         def gen():
index 591f32a..9c9486e 100644 (file)
@@ -48,6 +48,18 @@ class BinHexTestCase(unittest.TestCase):
 
         self.assertRaises(binhex.Error, binhex.binhex, self.fname3, self.fname2)
 
+    def test_binhex_line_endings(self):
+        # bpo-29566: Ensure the line endings are those for macOS 9
+        with open(self.fname1, 'wb') as f:
+            f.write(self.DATA)
+
+        binhex.binhex(self.fname1, self.fname2)
+
+        with open(self.fname2, 'rb') as fp:
+            contents = fp.read()
+
+        self.assertNotIn(b'\n', contents)
+
 def test_main():
     support.run_unittest(BinHexTestCase)
 
index 770e2c5..05568f2 100644 (file)
@@ -1034,6 +1034,7 @@ class BytesTest(BaseBytesTest, unittest.TestCase):
             c_char_p)
 
         PyBytes_FromFormat = pythonapi.PyBytes_FromFormat
+        PyBytes_FromFormat.argtypes = (c_char_p,)
         PyBytes_FromFormat.restype = py_object
 
         # basic tests
index 3d5dc47..9989b14 100644 (file)
@@ -794,17 +794,29 @@ class ClinicExternalTest(TestCase):
     maxDiff = None
 
     def test_external(self):
+        # bpo-42398: Test that the destination file is left unchanged if the
+        # content does not change. Moreover, check also that the file
+        # modification time does not change in this case.
         source = support.findfile('clinic.test')
         with open(source, 'r', encoding='utf-8') as f:
-            original = f.read()
-        with support.temp_dir() as testdir:
-            testfile = os.path.join(testdir, 'clinic.test.c')
+            orig_contents = f.read()
+
+        with support.temp_dir() as tmp_dir:
+            testfile = os.path.join(tmp_dir, 'clinic.test.c')
             with open(testfile, 'w', encoding='utf-8') as f:
-                f.write(original)
-            clinic.parse_file(testfile, force=True)
+                f.write(orig_contents)
+            old_mtime_ns = os.stat(testfile).st_mtime_ns
+
+            clinic.parse_file(testfile)
+
             with open(testfile, 'r', encoding='utf-8') as f:
-                result = f.read()
-            self.assertEqual(result, original)
+                new_contents = f.read()
+            new_mtime_ns = os.stat(testfile).st_mtime_ns
+
+        self.assertEqual(new_contents, orig_contents)
+        # Don't change the file modification time
+        # if the content does not change
+        self.assertEqual(new_mtime_ns, old_mtime_ns)
 
 
 if __name__ == "__main__":
index 54a3520..8d112a1 100644 (file)
@@ -2183,6 +2183,18 @@ class CharmapTest(unittest.TestCase):
             ("", len(allbytes))
         )
 
+        self.assertRaisesRegex(TypeError,
+            "character mapping must be in range\\(0x110000\\)",
+            codecs.charmap_decode,
+            b"\x00\x01\x02", "strict", {0: "A", 1: 'Bb', 2: -2}
+        )
+
+        self.assertRaisesRegex(TypeError,
+            "character mapping must be in range\\(0x110000\\)",
+            codecs.charmap_decode,
+            b"\x00\x01\x02", "strict", {0: "A", 1: 'Bb', 2: 999999999}
+        )
+
     def test_decode_with_int2int_map(self):
         a = ord('a')
         b = ord('b')
index a8d3337..057ec92 100644 (file)
@@ -195,6 +195,22 @@ class TestChainMap(unittest.TestCase):
              ('e', 55), ('f', 666), ('g', 777), ('h', 88888),
              ('i', 9999), ('j', 0)])
 
+    def test_iter_not_calling_getitem_on_maps(self):
+        class DictWithGetItem(UserDict):
+            def __init__(self, *args, **kwds):
+                self.called = False
+                UserDict.__init__(self, *args, **kwds)
+            def __getitem__(self, item):
+                self.called = True
+                UserDict.__getitem__(self, item)
+
+        d = DictWithGetItem(a=1)
+        c = ChainMap(d)
+        d.called = False
+
+        set(c)  # iterate over chain map
+        self.assertFalse(d.called, '__getitem__ was called')
+
     def test_dict_coercion(self):
         d = ChainMap(dict(a=1, b=2), dict(b=20, c=30))
         self.assertEqual(dict(d), dict(a=1, b=2, c=30))
@@ -1558,6 +1574,62 @@ class TestCollectionABCs(ABCTestCase):
         # coerce both to a real set then check equality
         self.assertSetEqual(set(s1), set(s2))
 
+    def test_Set_from_iterable(self):
+        """Verify _from_iterable overriden to an instance method works."""
+        class SetUsingInstanceFromIterable(MutableSet):
+            def __init__(self, values, created_by):
+                if not created_by:
+                    raise ValueError(f'created_by must be specified')
+                self.created_by = created_by
+                self._values = set(values)
+
+            def _from_iterable(self, values):
+                return type(self)(values, 'from_iterable')
+
+            def __contains__(self, value):
+                return value in self._values
+
+            def __iter__(self):
+                yield from self._values
+
+            def __len__(self):
+                return len(self._values)
+
+            def add(self, value):
+                self._values.add(value)
+
+            def discard(self, value):
+                self._values.discard(value)
+
+        impl = SetUsingInstanceFromIterable([1, 2, 3], 'test')
+
+        actual = impl - {1}
+        self.assertIsInstance(actual, SetUsingInstanceFromIterable)
+        self.assertEqual('from_iterable', actual.created_by)
+        self.assertEqual({2, 3}, actual)
+
+        actual = impl | {4}
+        self.assertIsInstance(actual, SetUsingInstanceFromIterable)
+        self.assertEqual('from_iterable', actual.created_by)
+        self.assertEqual({1, 2, 3, 4}, actual)
+
+        actual = impl & {2}
+        self.assertIsInstance(actual, SetUsingInstanceFromIterable)
+        self.assertEqual('from_iterable', actual.created_by)
+        self.assertEqual({2}, actual)
+
+        actual = impl ^ {3, 4}
+        self.assertIsInstance(actual, SetUsingInstanceFromIterable)
+        self.assertEqual('from_iterable', actual.created_by)
+        self.assertEqual({1, 2, 4}, actual)
+
+        # NOTE: ixor'ing with a list is important here: internally, __ixor__
+        # only calls _from_iterable if the other value isn't already a Set.
+        impl ^= [3, 4]
+        self.assertIsInstance(impl, SetUsingInstanceFromIterable)
+        self.assertEqual('test', impl.created_by)
+        self.assertEqual({1, 2, 4}, impl)
+
     def test_Set_interoperability_with_real_sets(self):
         # Issue: 8743
         class ListSet(Set):
index 59eabb0..ab68cdd 100644 (file)
@@ -311,6 +311,41 @@ class TestMessageAPI(TestEmailBase):
         g.flatten(msg)
         self.assertEqual(fullrepr, s.getvalue())
 
+    def test_nonascii_as_string_without_cte(self):
+        m = textwrap.dedent("""\
+            MIME-Version: 1.0
+            Content-type: text/plain; charset="iso-8859-1"
+
+            Test if non-ascii messages with no Content-Transfer-Encoding set
+            can be as_string'd:
+            Föö bär
+            """)
+        source = m.encode('iso-8859-1')
+        expected = textwrap.dedent("""\
+            MIME-Version: 1.0
+            Content-type: text/plain; charset="iso-8859-1"
+            Content-Transfer-Encoding: quoted-printable
+
+            Test if non-ascii messages with no Content-Transfer-Encoding set
+            can be as_string'd:
+            F=F6=F6 b=E4r
+            """)
+        msg = email.message_from_bytes(source)
+        self.assertEqual(msg.as_string(), expected)
+
+    def test_nonascii_as_string_without_content_type_and_cte(self):
+        m = textwrap.dedent("""\
+            MIME-Version: 1.0
+
+            Test if non-ascii messages with no Content-Type nor
+            Content-Transfer-Encoding set can be as_string'd:
+            Föö bär
+            """)
+        source = m.encode('iso-8859-1')
+        expected = source.decode('ascii', 'replace')
+        msg = email.message_from_bytes(source)
+        self.assertEqual(msg.as_string(), expected)
+
     def test_as_bytes(self):
         msg = self._msgobj('msg_01.txt')
         with openfile('msg_01.txt') as fp:
index 47f3711..8d125b5 100644 (file)
@@ -205,6 +205,7 @@ class ExceptionTests(unittest.TestCase):
         check(b'Python = "\xcf\xb3\xf2\xee\xed" +', 1, 18)
         check('x = "a', 1, 7)
         check('lambda x: x = 2', 1, 1)
+        check('f{a + b + c}', 1, 2)
 
         # Errors thrown by compile.c
         check('class foo:return 1', 1, 11)
index 35d7913..1d13443 100644 (file)
@@ -16,6 +16,15 @@ except ImportError:
                 raise TypeError('requires _testcapi.with_tp_del')
         return C
 
+try:
+    from _testcapi import without_gc
+except ImportError:
+    def without_gc(cls):
+        class C:
+            def __new__(cls, *args, **kwargs):
+                raise TypeError('requires _testcapi.without_gc')
+        return C
+
 from test import support
 
 
@@ -94,9 +103,11 @@ class SimpleBase(NonGCSimpleBase):
         assert self.id_ == id(self)
 
 
+@without_gc
 class NonGC(NonGCSimpleBase):
     __slots__ = ()
 
+@without_gc
 class NonGCResurrector(NonGCSimpleBase):
     __slots__ = ()
 
@@ -109,8 +120,14 @@ class NonGCResurrector(NonGCSimpleBase):
 class Simple(SimpleBase):
     pass
 
-class SimpleResurrector(NonGCResurrector, SimpleBase):
-    pass
+# Can't inherit from NonGCResurrector, in case importing without_gc fails.
+class SimpleResurrector(SimpleBase):
+
+    def side_effect(self):
+        """
+        Resurrect self by storing self in a class-wide list.
+        """
+        self.survivors.append(self)
 
 
 class TestBase:
@@ -178,6 +195,7 @@ class SimpleFinalizationTest(TestBase, unittest.TestCase):
             self.assert_survivors([])
         self.assertIs(wr(), None)
 
+    @support.cpython_only
     def test_non_gc(self):
         with SimpleBase.test():
             s = NonGC()
@@ -191,6 +209,7 @@ class SimpleFinalizationTest(TestBase, unittest.TestCase):
             self.assert_del_calls(ids)
             self.assert_survivors([])
 
+    @support.cpython_only
     def test_non_gc_resurrect(self):
         with SimpleBase.test():
             s = NonGCResurrector()
index d2744cd..9653e46 100644 (file)
@@ -428,13 +428,16 @@ class FormatTest(unittest.TestCase):
             localeconv = locale.localeconv()
             sep = localeconv['thousands_sep']
             point = localeconv['decimal_point']
+            grouping = localeconv['grouping']
 
             text = format(123456789, "n")
-            self.assertIn(sep, text)
+            if grouping:
+                self.assertIn(sep, text)
             self.assertEqual(text.replace(sep, ''), '123456789')
 
             text = format(1234.5, "n")
-            self.assertIn(sep, text)
+            if grouping:
+                self.assertIn(sep, text)
             self.assertIn(point, text)
             self.assertEqual(text.replace(sep, ''), '1234' + point + '5')
         finally:
index acb6391..38c9cb7 100644 (file)
@@ -581,9 +581,9 @@ class GCTests(unittest.TestCase):
         self.assertTrue(gc.is_tracked(UserInt()))
         self.assertTrue(gc.is_tracked([]))
         self.assertTrue(gc.is_tracked(set()))
-        self.assertFalse(gc.is_tracked(UserClassSlots()))
-        self.assertFalse(gc.is_tracked(UserFloatSlots()))
-        self.assertFalse(gc.is_tracked(UserIntSlots()))
+        self.assertTrue(gc.is_tracked(UserClassSlots()))
+        self.assertTrue(gc.is_tracked(UserFloatSlots()))
+        self.assertTrue(gc.is_tracked(UserIntSlots()))
 
     def test_is_finalized(self):
         # Objects not tracked by the always gc return false
index 44cb9a0..22c75ba 100644 (file)
@@ -51,11 +51,6 @@ if gdb_major_version < 7:
                             "embedding. Saw %s.%s:\n%s"
                             % (gdb_major_version, gdb_minor_version,
                                gdb_version))
-if (gdb_major_version, gdb_minor_version) >= (9, 2):
-    # gdb 9.2 on Fedora Rawhide is not reliable, see:
-    # * https://bugs.python.org/issue41473
-    # * https://bugzilla.redhat.com/show_bug.cgi?id=1866884
-    raise unittest.SkipTest("https://bugzilla.redhat.com/show_bug.cgi?id=1866884")
 
 if not sysconfig.is_python_build():
     raise unittest.SkipTest("test_gdb only works on source builds at the moment.")
index ec1acd4..c113e53 100644 (file)
@@ -13,7 +13,10 @@ from contextvars import ContextVar, Token
 from dataclasses import Field
 from functools import partial, partialmethod, cached_property
 from mailbox import Mailbox, _PartialFile
-from ctypes import Array, LibraryLoader
+try:
+    import ctypes
+except ImportError:
+    ctypes = None
 from difflib import SequenceMatcher
 from filecmp import dircmp
 from fileinput import FileInput
@@ -26,7 +29,7 @@ try:
 except ImportError:
     # multiprocessing.shared_memory is not available on e.g. Android
     ShareableList = None
-from multiprocessing.queues import SimpleQueue
+from multiprocessing.queues import SimpleQueue as MPSimpleQueue
 from os import DirEntry
 from re import Pattern, Match
 from types import GenericAlias, MappingProxyType, AsyncGeneratorType
@@ -44,45 +47,46 @@ V = TypeVar('V')
 
 class BaseTest(unittest.TestCase):
     """Test basics."""
+    generic_types = [type, tuple, list, dict, set, frozenset, enumerate,
+                     defaultdict, deque,
+                     SequenceMatcher,
+                     dircmp,
+                     FileInput,
+                     OrderedDict, Counter, UserDict, UserList,
+                     Pattern, Match,
+                     partial, partialmethod, cached_property,
+                     AbstractContextManager, AbstractAsyncContextManager,
+                     Awaitable, Coroutine,
+                     AsyncIterable, AsyncIterator,
+                     AsyncGenerator, Generator,
+                     Iterable, Iterator,
+                     Reversible,
+                     Container, Collection,
+                     Callable,
+                     Mailbox, _PartialFile,
+                     ContextVar, Token,
+                     Field,
+                     Set, MutableSet,
+                     Mapping, MutableMapping, MappingView,
+                     KeysView, ItemsView, ValuesView,
+                     Sequence, MutableSequence,
+                     MappingProxyType, AsyncGeneratorType,
+                     DirEntry,
+                     chain,
+                     TemporaryDirectory, SpooledTemporaryFile,
+                     Queue, SimpleQueue,
+                     _AssertRaisesContext,
+                     SplitResult, ParseResult,
+                     ValueProxy, ApplyResult,
+                     WeakSet, ReferenceType, ref,
+                     ShareableList, MPSimpleQueue,
+                     Future, _WorkItem,
+                     Morsel]
+    if ctypes is not None:
+        generic_types.extend((ctypes.Array, ctypes.LibraryLoader))
 
     def test_subscriptable(self):
-        for t in (type, tuple, list, dict, set, frozenset, enumerate,
-                  defaultdict, deque,
-                  SequenceMatcher,
-                  dircmp,
-                  FileInput,
-                  OrderedDict, Counter, UserDict, UserList,
-                  Pattern, Match,
-                  partial, partialmethod, cached_property,
-                  AbstractContextManager, AbstractAsyncContextManager,
-                  Awaitable, Coroutine,
-                  AsyncIterable, AsyncIterator,
-                  AsyncGenerator, Generator,
-                  Iterable, Iterator,
-                  Reversible,
-                  Container, Collection,
-                  Callable,
-                  Mailbox, _PartialFile,
-                  ContextVar, Token,
-                  Field,
-                  Set, MutableSet,
-                  Mapping, MutableMapping, MappingView,
-                  KeysView, ItemsView, ValuesView,
-                  Sequence, MutableSequence,
-                  MappingProxyType, AsyncGeneratorType,
-                  DirEntry,
-                  chain,
-                  TemporaryDirectory, SpooledTemporaryFile,
-                  Queue, SimpleQueue,
-                  _AssertRaisesContext,
-                  Array, LibraryLoader,
-                  SplitResult, ParseResult,
-                  ValueProxy, ApplyResult,
-                  WeakSet, ReferenceType, ref,
-                  ShareableList, SimpleQueue,
-                  Future, _WorkItem,
-                  Morsel,
-                  ):
+        for t in self.generic_types:
             if t is None:
                 continue
             tname = t.__name__
@@ -289,5 +293,20 @@ class BaseTest(unittest.TestCase):
         for generic_alias_property in ("__origin__", "__args__", "__parameters__"):
             self.assertIn(generic_alias_property, dir_of_gen_alias)
 
+    def test_weakref(self):
+        for t in self.generic_types:
+            if t is None:
+                continue
+            tname = t.__name__
+            with self.subTest(f"Testing {tname}"):
+                alias = t[int]
+                self.assertEqual(ref(alias)(), alias)
+
+    def test_no_kwargs(self):
+        # bpo-42576
+        with self.assertRaises(TypeError):
+            GenericAlias(bad=float)
+
+
 if __name__ == "__main__":
     unittest.main()
index 86f20a7..86f31a5 100644 (file)
@@ -24,17 +24,26 @@ from http.client import HTTPException
 # Were we compiled --with-pydebug or with #define Py_DEBUG?
 COMPILED_WITH_PYDEBUG = hasattr(sys, 'gettotalrefcount')
 
-c_hashlib = import_fresh_module('hashlib', fresh=['_hashlib'])
-py_hashlib = import_fresh_module('hashlib', blocked=['_hashlib'])
-
+# default builtin hash module
+default_builtin_hashes = {'md5', 'sha1', 'sha256', 'sha512', 'sha3', 'blake2'}
+# --with-builtin-hashlib-hashes override
 builtin_hashes = sysconfig.get_config_var("PY_BUILTIN_HASHLIB_HASHES")
 if builtin_hashes is None:
-    builtin_hashes = {'md5', 'sha1', 'sha256', 'sha512', 'sha3', 'blake2'}
+    builtin_hashes = default_builtin_hashes
 else:
     builtin_hashes = {
         m.strip() for m in builtin_hashes.strip('"').lower().split(",")
     }
 
+# hashlib with and without OpenSSL backend for PBKDF2
+# only import builtin_hashlib when all builtin hashes are available.
+# Otherwise import prints noise on stderr
+openssl_hashlib = import_fresh_module('hashlib', fresh=['_hashlib'])
+if builtin_hashes == default_builtin_hashes:
+    builtin_hashlib = import_fresh_module('hashlib', blocked=['_hashlib'])
+else:
+    builtin_hashlib = None
+
 try:
     from _hashlib import HASH, HASHXOF, openssl_md_meth_names
 except ImportError:
@@ -1030,16 +1039,16 @@ class KDFTests(unittest.TestCase):
                 iterations=1, dklen=None)
             self.assertEqual(out, self.pbkdf2_results['sha1'][0][0])
 
+    @unittest.skipIf(builtin_hashlib is None, "test requires builtin_hashlib")
     def test_pbkdf2_hmac_py(self):
-        self._test_pbkdf2_hmac(py_hashlib.pbkdf2_hmac, builtin_hashes)
+        self._test_pbkdf2_hmac(builtin_hashlib.pbkdf2_hmac, builtin_hashes)
 
-    @unittest.skipUnless(hasattr(c_hashlib, 'pbkdf2_hmac'),
+    @unittest.skipUnless(hasattr(openssl_hashlib, 'pbkdf2_hmac'),
                      '   test requires OpenSSL > 1.0')
     def test_pbkdf2_hmac_c(self):
-        self._test_pbkdf2_hmac(c_hashlib.pbkdf2_hmac, openssl_md_meth_names)
-
+        self._test_pbkdf2_hmac(openssl_hashlib.pbkdf2_hmac, openssl_md_meth_names)
 
-    @unittest.skipUnless(hasattr(c_hashlib, 'scrypt'),
+    @unittest.skipUnless(hasattr(hashlib, 'scrypt'),
                      '   test requires OpenSSL > 1.1')
     def test_scrypt(self):
         for password, salt, n, r, p, expected in self.scrypt_test_vectors:
index c442f55..8df0b52 100644 (file)
@@ -3,7 +3,7 @@
 Written by Cody A.W. Somerville <cody-somerville@ubuntu.com>,
 Josip Dzolonga, and Michael Otteneder for the 2007/08 GHOP contest.
 """
-
+from collections import OrderedDict
 from http.server import BaseHTTPRequestHandler, HTTPServer, \
      SimpleHTTPRequestHandler, CGIHTTPRequestHandler
 from http import server, HTTPStatus
@@ -19,7 +19,7 @@ import shutil
 import email.message
 import email.utils
 import html
-import http.client
+import http, http.client
 import urllib.parse
 import tempfile
 import time
@@ -586,6 +586,15 @@ print()
 print(os.environ["%s"])
 """
 
+cgi_file6 = """\
+#!%s
+import os
+
+print("Content-type: text/plain")
+print()
+print(repr(os.environ))
+"""
+
 
 @unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
         "This test can't be run reliably as root (issue #13308).")
@@ -664,6 +673,11 @@ class CGIHTTPServerTestCase(BaseTestCase):
             file5.write(cgi_file1 % self.pythonexe)
         os.chmod(self.file5_path, 0o777)
 
+        self.file6_path = os.path.join(self.cgi_dir, 'file6.py')
+        with open(self.file6_path, 'w', encoding='utf-8') as file6:
+            file6.write(cgi_file6 % self.pythonexe)
+        os.chmod(self.file6_path, 0o777)
+
         os.chdir(self.parent_dir)
 
     def tearDown(self):
@@ -683,6 +697,8 @@ class CGIHTTPServerTestCase(BaseTestCase):
                 os.remove(self.file4_path)
             if self.file5_path:
                 os.remove(self.file5_path)
+            if self.file6_path:
+                os.remove(self.file6_path)
             os.rmdir(self.cgi_child_dir)
             os.rmdir(self.cgi_dir)
             os.rmdir(self.cgi_dir_in_sub_dir)
@@ -816,6 +832,23 @@ class CGIHTTPServerTestCase(BaseTestCase):
         finally:
             CGIHTTPRequestHandler.cgi_directories.remove('/sub/dir/cgi-bin')
 
+    def test_accept(self):
+        browser_accept = \
+                    'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
+        tests = (
+            ((('Accept', browser_accept),), browser_accept),
+            ((), ''),
+            # Hack case to get two values for the one header
+            ((('Accept', 'text/html'), ('ACCEPT', 'text/plain')),
+               'text/html,text/plain'),
+        )
+        for headers, expected in tests:
+            headers = OrderedDict(headers)
+            with self.subTest(headers):
+                res = self.request('/cgi-bin/file6.py', 'GET', headers=headers)
+                self.assertEqual(http.HTTPStatus.OK, res.status)
+                expected = f"'HTTP_ACCEPT': {expected!r}"
+                self.assertIn(expected.encode('ascii'), res.read())
 
 
 class SocketlessRequestHandler(SimpleHTTPRequestHandler):
index e3e2be5..d9e26a3 100644 (file)
@@ -388,6 +388,7 @@ class TestRetrievingSourceCode(GetSourceBase):
                           ('ParrotDroppings', mod.ParrotDroppings),
                           ('StupidGit', mod.StupidGit),
                           ('Tit', mod.MalodorousPervert),
+                          ('WhichComments', mod.WhichComments),
                          ])
         tree = inspect.getclasstree([cls[1] for cls in classes])
         self.assertEqual(tree,
@@ -401,7 +402,8 @@ class TestRetrievingSourceCode(GetSourceBase):
                             [(mod.FesteringGob, (mod.MalodorousPervert,
                                                     mod.ParrotDroppings))
                              ]
-                            ]
+                            ],
+                            (mod.WhichComments, (object,),)
                            ]
                           ])
         tree = inspect.getclasstree([cls[1] for cls in classes], True)
@@ -413,7 +415,8 @@ class TestRetrievingSourceCode(GetSourceBase):
                             [(mod.FesteringGob, (mod.MalodorousPervert,
                                                     mod.ParrotDroppings))
                              ]
-                            ]
+                            ],
+                            (mod.WhichComments, (object,),)
                            ]
                           ])
 
@@ -644,6 +647,18 @@ class TestOneliners(GetSourceBase):
         # as argument to another function.
         self.assertSourceEqual(mod2.anonymous, 55, 55)
 
+class TestBlockComments(GetSourceBase):
+    fodderModule = mod
+
+    def test_toplevel_class(self):
+        self.assertSourceEqual(mod.WhichComments, 96, 114)
+
+    def test_class_method(self):
+        self.assertSourceEqual(mod.WhichComments.f, 99, 104)
+
+    def test_class_async_method(self):
+        self.assertSourceEqual(mod.WhichComments.asyncf, 109, 112)
+
 class TestBuggyCases(GetSourceBase):
     fodderModule = mod2
 
@@ -695,6 +710,17 @@ class TestBuggyCases(GetSourceBase):
             self.assertRaises(IOError, inspect.findsource, co)
             self.assertRaises(IOError, inspect.getsource, co)
 
+    def test_findsource_with_out_of_bounds_lineno(self):
+        mod_len = len(inspect.getsource(mod))
+        src = '\n' * 2* mod_len + "def f(): pass"
+        co = compile(src, mod.__file__, "exec")
+        g, l = {}, {}
+        eval(co, g, l)
+        func = l['f']
+        self.assertEqual(func.__code__.co_firstlineno, 1+2*mod_len)
+        with self.assertRaisesRegex(IOError, "lineno is out of bounds"):
+            inspect.findsource(func)
+
     def test_getsource_on_method(self):
         self.assertSourceEqual(mod2.ClassWithMethod.method, 118, 119)
 
@@ -4016,8 +4042,8 @@ def foo():
 
 def test_main():
     run_unittest(
-        TestDecorators, TestRetrievingSourceCode, TestOneliners, TestBuggyCases,
-        TestInterpreterStack, TestClassesAndFunctions, TestPredicates,
+        TestDecorators, TestRetrievingSourceCode, TestOneliners, TestBlockComments,
+        TestBuggyCases, TestInterpreterStack, TestClassesAndFunctions, TestPredicates,
         TestGetcallargsFunctions, TestGetcallargsMethods,
         TestGetcallargsUnboundMethods, TestGetattrStatic, TestGetGeneratorState,
         TestNoEOL, TestSignatureObject, TestSignatureBind, TestParameterObject,
index 53639e9..31b3899 100644 (file)
@@ -271,6 +271,16 @@ class TestIsInstanceIsSubclass(unittest.TestCase):
 
         self.assertEqual(True, issubclass(B(), int))
 
+    def test_infinite_recursion_in_bases(self):
+        class X:
+            @property
+            def __bases__(self):
+                return self.__bases__
+
+        self.assertRaises(RecursionError, issubclass, X(), int)
+        self.assertRaises(RecursionError, issubclass, int, X())
+        self.assertRaises(RecursionError, isinstance, 1, X())
+
 
 def blowstack(fxn, arg, compare_to):
     # Make sure that calling isinstance with a deeply nested tuple for its
index eaa6197..702cf08 100644 (file)
@@ -2290,7 +2290,7 @@ Samuele
 ...     "Count how many times the predicate is true"
 ...     return sum(map(pred, iterable))
 
->>> def padnone(iterable):
+>>> def pad_none(iterable):
 ...     "Returns the sequence elements and then returns None indefinitely"
 ...     return chain(iterable, repeat(None))
 
@@ -2460,7 +2460,7 @@ True
 >>> list(pairwise('a'))
 []
 
->>> list(islice(padnone('abc'), 0, 6))
+>>> list(islice(pad_none('abc'), 0, 6))
 ['a', 'b', 'c', None, None, None]
 
 >>> list(ncycles('abc', 3))
index 15c317e..e939d47 100644 (file)
@@ -1,8 +1,8 @@
 import unittest
-from test.support import check_warnings
+from test.support import check_warnings, import_fresh_module
 
 with check_warnings(("", PendingDeprecationWarning)):
-    from lib2to3.tests import load_tests
+    load_tests = import_fresh_module('lib2to3.tests', fresh=['lib2to3']).load_tests
 
 if __name__ == '__main__':
     unittest.main()
index 62759c0..410eae2 100644 (file)
@@ -1161,22 +1161,27 @@ class MemoryHandlerTest(BaseTest):
         class MockRaceConditionHandler:
             def __init__(self, mem_hdlr):
                 self.mem_hdlr = mem_hdlr
+                self.threads = []
 
             def removeTarget(self):
                 self.mem_hdlr.setTarget(None)
 
             def handle(self, msg):
-                t = threading.Thread(target=self.removeTarget)
-                t.daemon = True
-                t.start()
+                thread = threading.Thread(target=self.removeTarget)
+                self.threads.append(thread)
+                thread.start()
 
         target = MockRaceConditionHandler(self.mem_hdlr)
-        self.mem_hdlr.setTarget(target)
+        try:
+            self.mem_hdlr.setTarget(target)
 
-        for _ in range(10):
-            time.sleep(0.005)
-            self.mem_logger.info("not flushed")
-            self.mem_logger.warning("flushed")
+            for _ in range(10):
+                time.sleep(0.005)
+                self.mem_logger.info("not flushed")
+                self.mem_logger.warning("flushed")
+        finally:
+            for thread in target.threads:
+                support.join_thread(thread)
 
 
 class ExceptionFormatter(logging.Formatter):
index c813830..2adcd4b 100644 (file)
@@ -113,7 +113,7 @@ class NamedExpressionInvalidTest(unittest.TestCase):
             "assignment expression within a comprehension cannot be used in a class body"):
             exec(code, {}, {})
 
-    def test_named_expression_invalid_rebinding_comprehension_iteration_variable(self):
+    def test_named_expression_invalid_rebinding_list_comprehension_iteration_variable(self):
         cases = [
             ("Local reuse", 'i', "[i := 0 for i in range(5)]"),
             ("Nested reuse", 'j', "[[(j := 0) for i in range(5)] for j in range(5)]"),
@@ -130,7 +130,7 @@ class NamedExpressionInvalidTest(unittest.TestCase):
                 with self.assertRaisesRegex(SyntaxError, msg):
                     exec(code, {}, {})
 
-    def test_named_expression_invalid_rebinding_comprehension_inner_loop(self):
+    def test_named_expression_invalid_rebinding_list_comprehension_inner_loop(self):
         cases = [
             ("Inner reuse", 'j', "[i for i in range(5) if (j := 0) for j in range(5)]"),
             ("Inner unpacking reuse", 'j', "[i for i in range(5) if (j := 0) for j, k in [(0, 1)]]"),
@@ -145,7 +145,7 @@ class NamedExpressionInvalidTest(unittest.TestCase):
                 with self.assertRaisesRegex(SyntaxError, msg):
                     exec(f"lambda: {code}", {}) # Function scope
 
-    def test_named_expression_invalid_comprehension_iterable_expression(self):
+    def test_named_expression_invalid_list_comprehension_iterable_expression(self):
         cases = [
             ("Top level", "[i for i in (i := range(5))]"),
             ("Inside tuple", "[i for i in (2, 3, i := range(5))]"),
@@ -167,6 +167,60 @@ class NamedExpressionInvalidTest(unittest.TestCase):
                 with self.assertRaisesRegex(SyntaxError, msg):
                     exec(f"lambda: {code}", {}) # Function scope
 
+    def test_named_expression_invalid_rebinding_set_comprehension_iteration_variable(self):
+        cases = [
+            ("Local reuse", 'i', "{i := 0 for i in range(5)}"),
+            ("Nested reuse", 'j', "{{(j := 0) for i in range(5)} for j in range(5)}"),
+            ("Reuse inner loop target", 'j', "{(j := 0) for i in range(5) for j in range(5)}"),
+            ("Unpacking reuse", 'i', "{i := 0 for i, j in {(0, 1)}}"),
+            ("Reuse in loop condition", 'i', "{i+1 for i in range(5) if (i := 0)}"),
+            ("Unreachable reuse", 'i', "{False or (i:=0) for i in range(5)}"),
+            ("Unreachable nested reuse", 'i',
+                "{(i, j) for i in range(5) for j in range(5) if True or (i:=10)}"),
+        ]
+        for case, target, code in cases:
+            msg = f"assignment expression cannot rebind comprehension iteration variable '{target}'"
+            with self.subTest(case=case):
+                with self.assertRaisesRegex(SyntaxError, msg):
+                    exec(code, {}, {})
+
+    def test_named_expression_invalid_rebinding_set_comprehension_inner_loop(self):
+        cases = [
+            ("Inner reuse", 'j', "{i for i in range(5) if (j := 0) for j in range(5)}"),
+            ("Inner unpacking reuse", 'j', "{i for i in range(5) if (j := 0) for j, k in {(0, 1)}}"),
+        ]
+        for case, target, code in cases:
+            msg = f"comprehension inner loop cannot rebind assignment expression target '{target}'"
+            with self.subTest(case=case):
+                with self.assertRaisesRegex(SyntaxError, msg):
+                    exec(code, {}) # Module scope
+                with self.assertRaisesRegex(SyntaxError, msg):
+                    exec(code, {}, {}) # Class scope
+                with self.assertRaisesRegex(SyntaxError, msg):
+                    exec(f"lambda: {code}", {}) # Function scope
+
+    def test_named_expression_invalid_set_comprehension_iterable_expression(self):
+        cases = [
+            ("Top level", "{i for i in (i := range(5))}"),
+            ("Inside tuple", "{i for i in (2, 3, i := range(5))}"),
+            ("Inside list", "{i for i in {2, 3, i := range(5)}}"),
+            ("Different name", "{i for i in (j := range(5))}"),
+            ("Lambda expression", "{i for i in (lambda:(j := range(5)))()}"),
+            ("Inner loop", "{i for i in range(5) for j in (i := range(5))}"),
+            ("Nested comprehension", "{i for i in {j for j in (k := range(5))}}"),
+            ("Nested comprehension condition", "{i for i in {j for j in range(5) if (j := True)}}"),
+            ("Nested comprehension body", "{i for i in {(j := True) for j in range(5)}}"),
+        ]
+        msg = "assignment expression cannot be used in a comprehension iterable expression"
+        for case, code in cases:
+            with self.subTest(case=case):
+                with self.assertRaisesRegex(SyntaxError, msg):
+                    exec(code, {}) # Module scope
+                with self.assertRaisesRegex(SyntaxError, msg):
+                    exec(code, {}, {}) # Class scope
+                with self.assertRaisesRegex(SyntaxError, msg):
+                    exec(f"lambda: {code}", {}) # Function scope
+
 
 class NamedExpressionAssignmentTest(unittest.TestCase):
 
@@ -513,6 +567,15 @@ spam()"""
             self.assertEqual(nonlocal_var, None)
         f()
 
+    def test_named_expression_scope_in_genexp(self):
+        a = 1
+        b = [1, 2, 3, 4]
+        genexp = (c := i + a for i in b)
+
+        self.assertNotIn("c", locals())
+        for idx, elem in enumerate(genexp):
+            self.assertEqual(elem, b[idx] + a)
+
 
 if __name__ == "__main__":
     unittest.main()
index 7913e91..55543de 100644 (file)
@@ -522,6 +522,13 @@ class TestBuglets(unittest.TestCase):
         with self.assertRaises(ValueError):
             f()
 
+    def test_bpo_42057(self):
+        for i in range(10):
+            try:
+                raise Exception
+            except Exception or Exception:
+                pass
+
 
 if __name__ == "__main__":
     unittest.main()
index f993525..9701d4f 100644 (file)
@@ -1,3 +1,4 @@
+import sysconfig
 import textwrap
 import unittest
 from distutils.tests.support import TempdirManager
@@ -7,6 +8,11 @@ from test import test_tools
 from test import support
 from test.support.script_helper import assert_python_ok
 
+_py_cflags_nodist = sysconfig.get_config_var('PY_CFLAGS_NODIST')
+_pgo_flag = sysconfig.get_config_var('PGO_PROF_USE_FLAG')
+if _pgo_flag and _py_cflags_nodist and _pgo_flag in _py_cflags_nodist:
+    raise unittest.SkipTest("peg_generator test disabled under PGO build")
+
 test_tools.skip_if_missing("peg_generator")
 with test_tools.imports_under_tool("peg_generator"):
     from pegen.grammar_parser import GeneratedParser as GrammarParser
index a5c35df..a3b06fe 100644 (file)
@@ -237,7 +237,10 @@ class PlatformTest(unittest.TestCase):
             # On Snow Leopard, sw_vers reports 10.6.0 as 10.6
             if len_diff > 0:
                 expect_list.extend(['0'] * len_diff)
-            self.assertEqual(result_list, expect_list)
+            # For compatibility with older binaries, macOS 11.x may report
+            # itself as '10.16' rather than '11.x.y'.
+            if result_list != ['10', '16']:
+                self.assertEqual(result_list, expect_list)
 
             # res[1] claims to contain
             # (version, dev_stage, non_release_version)
@@ -245,7 +248,7 @@ class PlatformTest(unittest.TestCase):
             self.assertEqual(res[1], ('', '', ''))
 
             if sys.byteorder == 'little':
-                self.assertIn(res[2], ('i386', 'x86_64'))
+                self.assertIn(res[2], ('i386', 'x86_64', 'arm64'))
             else:
                 self.assertEqual(res[2], 'PowerPC')
 
index e82a53c..9e53305 100644 (file)
@@ -2,6 +2,7 @@
 import copy
 import operator
 import pickle
+import struct
 import unittest
 import plistlib
 import os
@@ -105,6 +106,298 @@ TESTDATA={
         AAABOQ=='''),
 }
 
+XML_PLIST_WITH_ENTITY=b'''\
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd" [
+   <!ENTITY entity "replacement text">
+  ]>
+<plist version="1.0">
+  <dict>
+    <key>A</key>
+    <string>&entity;</string>
+  </dict>
+</plist>
+'''
+
+INVALID_BINARY_PLISTS = [
+    ('too short data',
+        b''
+    ),
+    ('too large offset_table_offset and offset_size = 1',
+        b'\x00\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x2a'
+    ),
+    ('too large offset_table_offset and nonstandard offset_size',
+        b'\x00\x00\x00\x08'
+        b'\x00\x00\x00\x00\x00\x00\x03\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x2c'
+    ),
+    ('integer overflow in offset_table_offset',
+        b'\x00\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\xff\xff\xff\xff\xff\xff\xff\xff'
+    ),
+    ('too large top_object',
+        b'\x00\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x09'
+    ),
+    ('integer overflow in top_object',
+        b'\x00\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\xff\xff\xff\xff\xff\xff\xff\xff'
+        b'\x00\x00\x00\x00\x00\x00\x00\x09'
+    ),
+    ('too large num_objects and offset_size = 1',
+        b'\x00\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\xff'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x09'
+    ),
+    ('too large num_objects and nonstandard offset_size',
+        b'\x00\x00\x00\x08'
+        b'\x00\x00\x00\x00\x00\x00\x03\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\xff'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x09'
+    ),
+    ('extremally large num_objects (32 bit)',
+        b'\x00\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x7f\xff\xff\xff'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x09'
+    ),
+    ('extremally large num_objects (64 bit)',
+        b'\x00\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\xff\xff\xff\xff\xff'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x09'
+    ),
+    ('integer overflow in num_objects',
+        b'\x00\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\xff\xff\xff\xff\xff\xff\xff\xff'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x09'
+    ),
+    ('offset_size = 0',
+        b'\x00\x08'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x09'
+    ),
+    ('ref_size = 0',
+        b'\xa1\x01\x00\x08\x0a'
+        b'\x00\x00\x00\x00\x00\x00\x01\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x02'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x0b'
+    ),
+    ('too large offset',
+        b'\x00\x2a'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x09'
+    ),
+    ('integer overflow in offset',
+        b'\x00\xff\xff\xff\xff\xff\xff\xff\xff'
+        b'\x00\x00\x00\x00\x00\x00\x08\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x09'
+    ),
+    ('too large array size',
+        b'\xaf\x00\x01\xff\x00\x08\x0c'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x02'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x0d'
+    ),
+    ('extremally large array size (32-bit)',
+        b'\xaf\x02\x7f\xff\xff\xff\x01\x00\x08\x0f'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x02'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x10'
+    ),
+    ('extremally large array size (64-bit)',
+        b'\xaf\x03\x00\x00\x00\xff\xff\xff\xff\xff\x01\x00\x08\x13'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x02'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x14'
+    ),
+    ('integer overflow in array size',
+        b'\xaf\x03\xff\xff\xff\xff\xff\xff\xff\xff\x01\x00\x08\x13'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x02'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x14'
+    ),
+    ('too large reference index',
+        b'\xa1\x02\x00\x08\x0a'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x02'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x0b'
+    ),
+    ('integer overflow in reference index',
+        b'\xa1\xff\xff\xff\xff\xff\xff\xff\xff\x00\x08\x11'
+        b'\x00\x00\x00\x00\x00\x00\x01\x08'
+        b'\x00\x00\x00\x00\x00\x00\x00\x02'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x12'
+    ),
+    ('too large bytes size',
+        b'\x4f\x00\x23\x41\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x0c'
+    ),
+    ('extremally large bytes size (32-bit)',
+        b'\x4f\x02\x7f\xff\xff\xff\x41\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x0f'
+    ),
+    ('extremally large bytes size (64-bit)',
+        b'\x4f\x03\x00\x00\x00\xff\xff\xff\xff\xff\x41\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x13'
+    ),
+    ('integer overflow in bytes size',
+        b'\x4f\x03\xff\xff\xff\xff\xff\xff\xff\xff\x41\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x13'
+    ),
+    ('too large ASCII size',
+        b'\x5f\x00\x23\x41\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x0c'
+    ),
+    ('extremally large ASCII size (32-bit)',
+        b'\x5f\x02\x7f\xff\xff\xff\x41\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x0f'
+    ),
+    ('extremally large ASCII size (64-bit)',
+        b'\x5f\x03\x00\x00\x00\xff\xff\xff\xff\xff\x41\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x13'
+    ),
+    ('integer overflow in ASCII size',
+        b'\x5f\x03\xff\xff\xff\xff\xff\xff\xff\xff\x41\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x13'
+    ),
+    ('invalid ASCII',
+        b'\x51\xff\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x0a'
+    ),
+    ('too large UTF-16 size',
+        b'\x6f\x00\x13\x20\xac\x00\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x0e'
+    ),
+    ('extremally large UTF-16 size (32-bit)',
+        b'\x6f\x02\x4f\xff\xff\xff\x20\xac\x00\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x11'
+    ),
+    ('extremally large UTF-16 size (64-bit)',
+        b'\x6f\x03\x00\x00\x00\xff\xff\xff\xff\xff\x20\xac\x00\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x15'
+    ),
+    ('integer overflow in UTF-16 size',
+        b'\x6f\x03\xff\xff\xff\xff\xff\xff\xff\xff\x20\xac\x00\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x15'
+    ),
+    ('invalid UTF-16',
+        b'\x61\xd8\x00\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x0b'
+    ),
+    ('non-hashable key',
+        b'\xd1\x01\x01\xa0\x08\x0b'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x02'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x0c'
+    ),
+    ('too large datetime (datetime overflow)',
+        b'\x33\x42\x50\x00\x00\x00\x00\x00\x00\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x11'
+    ),
+    ('too large datetime (timedelta overflow)',
+        b'\x33\x42\xe0\x00\x00\x00\x00\x00\x00\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x11'
+    ),
+    ('invalid datetime (Infinity)',
+        b'\x33\x7f\xf0\x00\x00\x00\x00\x00\x00\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x11'
+    ),
+    ('invalid datetime (NaN)',
+        b'\x33\x7f\xf8\x00\x00\x00\x00\x00\x00\x08'
+        b'\x00\x00\x00\x00\x00\x00\x01\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x01'
+        b'\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x00\x00\x00\x00\x00\x00\x00\x11'
+    ),
+]
+
 
 class TestPlistlib(unittest.TestCase):
 
@@ -484,6 +777,19 @@ class TestPlistlib(unittest.TestCase):
         self.assertRaises(ValueError, plistlib.loads,
                           b"<plist><integer>not real</integer></plist>")
 
+    def test_integer_notations(self):
+        pl = b"<plist><integer>456</integer></plist>"
+        value = plistlib.loads(pl)
+        self.assertEqual(value, 456)
+
+        pl = b"<plist><integer>0xa</integer></plist>"
+        value = plistlib.loads(pl)
+        self.assertEqual(value, 10)
+
+        pl = b"<plist><integer>0123</integer></plist>"
+        value = plistlib.loads(pl)
+        self.assertEqual(value, 123)
+
     def test_xml_encodings(self):
         base = TESTDATA[plistlib.FMT_XML]
 
@@ -523,9 +829,29 @@ class TestPlistlib(unittest.TestCase):
         with self.assertRaises(OverflowError):
             plistlib.dumps(huge_uid, fmt=plistlib.FMT_BINARY)
 
+    def test_xml_plist_with_entity_decl(self):
+        with self.assertRaisesRegex(plistlib.InvalidFileException,
+                                    "XML entity declarations are not supported"):
+            plistlib.loads(XML_PLIST_WITH_ENTITY, fmt=plistlib.FMT_XML)
+
 
 class TestBinaryPlistlib(unittest.TestCase):
 
+    @staticmethod
+    def decode(*objects, offset_size=1, ref_size=1):
+        data = [b'bplist00']
+        offset = 8
+        offsets = []
+        for x in objects:
+            offsets.append(offset.to_bytes(offset_size, 'big'))
+            data.append(x)
+            offset += len(x)
+        tail = struct.pack('>6xBBQQQ', offset_size, ref_size,
+                           len(objects), 0, offset)
+        data.extend(offsets)
+        data.append(tail)
+        return plistlib.loads(b''.join(data), fmt=plistlib.FMT_BINARY)
+
     def test_nonstandard_refs_size(self):
         # Issue #21538: Refs and offsets are 24-bit integers
         data = (b'bplist00'
@@ -540,7 +866,7 @@ class TestBinaryPlistlib(unittest.TestCase):
 
     def test_dump_duplicates(self):
         # Test effectiveness of saving duplicated objects
-        for x in (None, False, True, 12345, 123.45, 'abcde', b'abcde',
+        for x in (None, False, True, 12345, 123.45, 'abcde', 'абвгд', b'abcde',
                   datetime.datetime(2004, 10, 26, 10, 33, 33),
                   bytearray(b'abcde'), [12, 345], (12, 345), {'12': 345}):
             with self.subTest(x=x):
@@ -577,6 +903,20 @@ class TestBinaryPlistlib(unittest.TestCase):
         b = plistlib.loads(plistlib.dumps(a, fmt=plistlib.FMT_BINARY))
         self.assertIs(b['x'], b)
 
+    def test_deep_nesting(self):
+        for N in [300, 100000]:
+            chunks = [b'\xa1' + (i + 1).to_bytes(4, 'big') for i in range(N)]
+            try:
+                result = self.decode(*chunks, b'\x54seed', offset_size=4, ref_size=4)
+            except RecursionError:
+                pass
+            else:
+                for i in range(N):
+                    self.assertIsInstance(result, list)
+                    self.assertEqual(len(result), 1)
+                    result = result[0]
+                self.assertEqual(result, 'seed')
+
     def test_large_timestamp(self):
         # Issue #26709: 32-bit timestamp out of range
         for ts in -2**31-1, 2**31:
@@ -586,55 +926,37 @@ class TestBinaryPlistlib(unittest.TestCase):
                 data = plistlib.dumps(d, fmt=plistlib.FMT_BINARY)
                 self.assertEqual(plistlib.loads(data), d)
 
+    def test_load_singletons(self):
+        self.assertIs(self.decode(b'\x00'), None)
+        self.assertIs(self.decode(b'\x08'), False)
+        self.assertIs(self.decode(b'\x09'), True)
+        self.assertEqual(self.decode(b'\x0f'), b'')
+
+    def test_load_int(self):
+        self.assertEqual(self.decode(b'\x10\x00'), 0)
+        self.assertEqual(self.decode(b'\x10\xfe'), 0xfe)
+        self.assertEqual(self.decode(b'\x11\xfe\xdc'), 0xfedc)
+        self.assertEqual(self.decode(b'\x12\xfe\xdc\xba\x98'), 0xfedcba98)
+        self.assertEqual(self.decode(b'\x13\x01\x23\x45\x67\x89\xab\xcd\xef'),
+                         0x0123456789abcdef)
+        self.assertEqual(self.decode(b'\x13\xfe\xdc\xba\x98\x76\x54\x32\x10'),
+                         -0x123456789abcdf0)
+
+    def test_unsupported(self):
+        unsupported = [*range(1, 8), *range(10, 15),
+                       0x20, 0x21, *range(0x24, 0x33), *range(0x34, 0x40)]
+        for i in [0x70, 0x90, 0xb0, 0xc0, 0xe0, 0xf0]:
+            unsupported.extend(i + j for j in range(16))
+        for token in unsupported:
+            with self.subTest(f'token {token:02x}'):
+                with self.assertRaises(plistlib.InvalidFileException):
+                    self.decode(bytes([token]) + b'\x00'*16)
+
     def test_invalid_binary(self):
-        for data in [
-                # too short data
-                b'',
-                # too large offset_table_offset and nonstandard offset_size
-                b'\x00\x08'
-                b'\x00\x00\x00\x00\x00\x00\x03\x01'
-                b'\x00\x00\x00\x00\x00\x00\x00\x01'
-                b'\x00\x00\x00\x00\x00\x00\x00\x00'
-                b'\x00\x00\x00\x00\x00\x00\x00\x2a',
-                # integer overflow in offset_table_offset
-                b'\x00\x08'
-                b'\x00\x00\x00\x00\x00\x00\x01\x01'
-                b'\x00\x00\x00\x00\x00\x00\x00\x01'
-                b'\x00\x00\x00\x00\x00\x00\x00\x00'
-                b'\xff\xff\xff\xff\xff\xff\xff\xff',
-                # offset_size = 0
-                b'\x00\x08'
-                b'\x00\x00\x00\x00\x00\x00\x00\x01'
-                b'\x00\x00\x00\x00\x00\x00\x00\x01'
-                b'\x00\x00\x00\x00\x00\x00\x00\x00'
-                b'\x00\x00\x00\x00\x00\x00\x00\x09',
-                # ref_size = 0
-                b'\xa1\x01\x00\x08\x0a'
-                b'\x00\x00\x00\x00\x00\x00\x01\x00'
-                b'\x00\x00\x00\x00\x00\x00\x00\x02'
-                b'\x00\x00\x00\x00\x00\x00\x00\x00'
-                b'\x00\x00\x00\x00\x00\x00\x00\x0b',
-                # integer overflow in offset
-                b'\x00\xff\xff\xff\xff\xff\xff\xff\xff'
-                b'\x00\x00\x00\x00\x00\x00\x08\x01'
-                b'\x00\x00\x00\x00\x00\x00\x00\x01'
-                b'\x00\x00\x00\x00\x00\x00\x00\x00'
-                b'\x00\x00\x00\x00\x00\x00\x00\x09',
-                # invalid ASCII
-                b'\x51\xff\x08'
-                b'\x00\x00\x00\x00\x00\x00\x01\x01'
-                b'\x00\x00\x00\x00\x00\x00\x00\x01'
-                b'\x00\x00\x00\x00\x00\x00\x00\x00'
-                b'\x00\x00\x00\x00\x00\x00\x00\x0a',
-                # invalid UTF-16
-                b'\x61\xd8\x00\x08'
-                b'\x00\x00\x00\x00\x00\x00\x01\x01'
-                b'\x00\x00\x00\x00\x00\x00\x00\x01'
-                b'\x00\x00\x00\x00\x00\x00\x00\x00'
-                b'\x00\x00\x00\x00\x00\x00\x00\x0b',
-                ]:
-            with self.assertRaises(plistlib.InvalidFileException):
-                plistlib.loads(b'bplist00' + data, fmt=plistlib.FMT_BINARY)
+        for name, data in INVALID_BINARY_PLISTS:
+            with self.subTest(name):
+                with self.assertRaises(plistlib.InvalidFileException):
+                    plistlib.loads(b'bplist00' + data, fmt=plistlib.FMT_BINARY)
 
 
 class TestKeyedArchive(unittest.TestCase):
index be121ae..bfbcbab 100644 (file)
@@ -1045,7 +1045,7 @@ class PosixTester(unittest.TestCase):
         if sys.platform == 'darwin':
             import sysconfig
             dt = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') or '10.0'
-            if tuple(int(n) for n in dt.split('.')[0:2]) < (10, 6):
+            if tuple(int(n) for n in str(dt).split('.')[0:2]) < (10, 6):
                 raise unittest.SkipTest("getgroups(2) is broken prior to 10.6")
 
         # 'id -G' and 'os.getgroups()' should return the same
@@ -1905,6 +1905,233 @@ class TestPosixSpawnP(unittest.TestCase, _PosixSpawnMixin):
         assert_python_ok(*args, PATH=path)
 
 
+@unittest.skipUnless(sys.platform == "darwin", "test weak linking on macOS")
+class TestPosixWeaklinking(unittest.TestCase):
+    # These test cases verify that weak linking support on macOS works
+    # as expected. These cases only test new behaviour introduced by weak linking,
+    # regular behaviour is tested by the normal test cases.
+    #
+    # See the section on Weak Linking in Mac/README.txt for more information.
+    def setUp(self):
+        import sysconfig
+        import platform
+
+        config_vars = sysconfig.get_config_vars()
+        self.available = { nm for nm in config_vars if nm.startswith("HAVE_") and config_vars[nm] }
+        self.mac_ver = tuple(int(part) for part in platform.mac_ver()[0].split("."))
+
+    def _verify_available(self, name):
+        if name not in self.available:
+            raise unittest.SkipTest(f"{name} not weak-linked")
+
+    def test_pwritev(self):
+        self._verify_available("HAVE_PWRITEV")
+        if self.mac_ver >= (10, 16):
+            self.assertTrue(hasattr(os, "pwritev"), "os.pwritev is not available")
+            self.assertTrue(hasattr(os, "preadv"), "os.readv is not available")
+
+        else:
+            self.assertFalse(hasattr(os, "pwritev"), "os.pwritev is available")
+            self.assertFalse(hasattr(os, "preadv"), "os.readv is available")
+
+    def test_stat(self):
+        self._verify_available("HAVE_FSTATAT")
+        if self.mac_ver >= (10, 10):
+            self.assertIn("HAVE_FSTATAT", posix._have_functions)
+
+        else:
+            self.assertNotIn("HAVE_FSTATAT", posix._have_functions)
+
+            with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
+                os.stat("file", dir_fd=0)
+
+    def test_access(self):
+        self._verify_available("HAVE_FACCESSAT")
+        if self.mac_ver >= (10, 10):
+            self.assertIn("HAVE_FACCESSAT", posix._have_functions)
+
+        else:
+            self.assertNotIn("HAVE_FACCESSAT", posix._have_functions)
+
+            with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
+                os.access("file", os.R_OK, dir_fd=0)
+
+            with self.assertRaisesRegex(NotImplementedError, "follow_symlinks unavailable"):
+                os.access("file", os.R_OK, follow_symlinks=False)
+
+            with self.assertRaisesRegex(NotImplementedError, "effective_ids unavailable"):
+                os.access("file", os.R_OK, effective_ids=True)
+
+    def test_chmod(self):
+        self._verify_available("HAVE_FCHMODAT")
+        if self.mac_ver >= (10, 10):
+            self.assertIn("HAVE_FCHMODAT", posix._have_functions)
+
+        else:
+            self.assertNotIn("HAVE_FCHMODAT", posix._have_functions)
+            self.assertIn("HAVE_LCHMOD", posix._have_functions)
+
+            with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
+                os.chmod("file", 0o644, dir_fd=0)
+
+    def test_chown(self):
+        self._verify_available("HAVE_FCHOWNAT")
+        if self.mac_ver >= (10, 10):
+            self.assertIn("HAVE_FCHOWNAT", posix._have_functions)
+
+        else:
+            self.assertNotIn("HAVE_FCHOWNAT", posix._have_functions)
+            self.assertIn("HAVE_LCHOWN", posix._have_functions)
+
+            with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
+                os.chown("file", 0, 0, dir_fd=0)
+
+    def test_link(self):
+        self._verify_available("HAVE_LINKAT")
+        if self.mac_ver >= (10, 10):
+            self.assertIn("HAVE_LINKAT", posix._have_functions)
+
+        else:
+            self.assertNotIn("HAVE_LINKAT", posix._have_functions)
+
+            with self.assertRaisesRegex(NotImplementedError, "src_dir_fd unavailable"):
+                os.link("source", "target",  src_dir_fd=0)
+
+            with self.assertRaisesRegex(NotImplementedError, "dst_dir_fd unavailable"):
+                os.link("source", "target",  dst_dir_fd=0)
+
+            with self.assertRaisesRegex(NotImplementedError, "src_dir_fd unavailable"):
+                os.link("source", "target",  src_dir_fd=0, dst_dir_fd=0)
+
+            # issue 41355: !HAVE_LINKAT code path ignores the follow_symlinks flag
+            with support.temp_dir() as base_path:
+                link_path = os.path.join(base_path, "link")
+                target_path = os.path.join(base_path, "target")
+                source_path = os.path.join(base_path, "source")
+
+                with open(source_path, "w") as fp:
+                    fp.write("data")
+
+                os.symlink("target", link_path)
+
+                # Calling os.link should fail in the link(2) call, and
+                # should not reject *follow_symlinks* (to match the
+                # behaviour you'd get when building on a platform without
+                # linkat)
+                with self.assertRaises(FileExistsError):
+                    os.link(source_path, link_path, follow_symlinks=True)
+
+                with self.assertRaises(FileExistsError):
+                    os.link(source_path, link_path, follow_symlinks=False)
+
+
+    def test_listdir_scandir(self):
+        self._verify_available("HAVE_FDOPENDIR")
+        if self.mac_ver >= (10, 10):
+            self.assertIn("HAVE_FDOPENDIR", posix._have_functions)
+
+        else:
+            self.assertNotIn("HAVE_FDOPENDIR", posix._have_functions)
+
+            with self.assertRaisesRegex(TypeError, "listdir: path should be string, bytes, os.PathLike or None, not int"):
+                os.listdir(0)
+
+            with self.assertRaisesRegex(TypeError, "scandir: path should be string, bytes, os.PathLike or None, not int"):
+                os.scandir(0)
+
+    def test_mkdir(self):
+        self._verify_available("HAVE_MKDIRAT")
+        if self.mac_ver >= (10, 10):
+            self.assertIn("HAVE_MKDIRAT", posix._have_functions)
+
+        else:
+            self.assertNotIn("HAVE_MKDIRAT", posix._have_functions)
+
+            with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
+                os.mkdir("dir", dir_fd=0)
+
+    def test_rename_replace(self):
+        self._verify_available("HAVE_RENAMEAT")
+        if self.mac_ver >= (10, 10):
+            self.assertIn("HAVE_RENAMEAT", posix._have_functions)
+
+        else:
+            self.assertNotIn("HAVE_RENAMEAT", posix._have_functions)
+
+            with self.assertRaisesRegex(NotImplementedError, "src_dir_fd and dst_dir_fd unavailable"):
+                os.rename("a", "b", src_dir_fd=0)
+
+            with self.assertRaisesRegex(NotImplementedError, "src_dir_fd and dst_dir_fd unavailable"):
+                os.rename("a", "b", dst_dir_fd=0)
+
+            with self.assertRaisesRegex(NotImplementedError, "src_dir_fd and dst_dir_fd unavailable"):
+                os.replace("a", "b", src_dir_fd=0)
+
+            with self.assertRaisesRegex(NotImplementedError, "src_dir_fd and dst_dir_fd unavailable"):
+                os.replace("a", "b", dst_dir_fd=0)
+
+    def test_unlink_rmdir(self):
+        self._verify_available("HAVE_UNLINKAT")
+        if self.mac_ver >= (10, 10):
+            self.assertIn("HAVE_UNLINKAT", posix._have_functions)
+
+        else:
+            self.assertNotIn("HAVE_UNLINKAT", posix._have_functions)
+
+            with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
+                os.unlink("path", dir_fd=0)
+
+            with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
+                os.rmdir("path", dir_fd=0)
+
+    def test_open(self):
+        self._verify_available("HAVE_OPENAT")
+        if self.mac_ver >= (10, 10):
+            self.assertIn("HAVE_OPENAT", posix._have_functions)
+
+        else:
+            self.assertNotIn("HAVE_OPENAT", posix._have_functions)
+
+            with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
+                os.open("path", os.O_RDONLY, dir_fd=0)
+
+    def test_readlink(self):
+        self._verify_available("HAVE_READLINKAT")
+        if self.mac_ver >= (10, 10):
+            self.assertIn("HAVE_READLINKAT", posix._have_functions)
+
+        else:
+            self.assertNotIn("HAVE_READLINKAT", posix._have_functions)
+
+            with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
+                os.readlink("path",  dir_fd=0)
+
+    def test_symlink(self):
+        self._verify_available("HAVE_SYMLINKAT")
+        if self.mac_ver >= (10, 10):
+            self.assertIn("HAVE_SYMLINKAT", posix._have_functions)
+
+        else:
+            self.assertNotIn("HAVE_SYMLINKAT", posix._have_functions)
+
+            with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
+                os.symlink("a", "b",  dir_fd=0)
+
+    def test_utime(self):
+        self._verify_available("HAVE_FUTIMENS")
+        self._verify_available("HAVE_UTIMENSAT")
+        if self.mac_ver >= (10, 13):
+            self.assertIn("HAVE_FUTIMENS", posix._have_functions)
+            self.assertIn("HAVE_UTIMENSAT", posix._have_functions)
+
+        else:
+            self.assertNotIn("HAVE_FUTIMENS", posix._have_functions)
+            self.assertNotIn("HAVE_UTIMENSAT", posix._have_functions)
+
+            with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
+                os.utime("path", dir_fd=0)
+
+
 def test_main():
     try:
         support.run_unittest(
@@ -1912,6 +2139,7 @@ def test_main():
             PosixGroupsTester,
             TestPosixSpawn,
             TestPosixSpawnP,
+            TestPosixWeaklinking
         )
     finally:
         support.reap_children()
index 01a8a6e..2336498 100644 (file)
@@ -6,7 +6,7 @@ import unittest
 import os
 from difflib import unified_diff
 from io import StringIO
-from test.support import TESTFN, run_unittest, unlink
+from test.support import TESTFN, run_unittest, unlink, temp_dir, change_cwd
 from contextlib import contextmanager
 
 import profile
@@ -111,6 +111,20 @@ class ProfileTest(unittest.TestCase):
         assert_python_ok('-m', self.profilermodule.__name__,
                          '-m', 'timeit', '-n', '1')
 
+    def test_output_file_when_changing_directory(self):
+        with temp_dir() as tmpdir, change_cwd(tmpdir):
+            os.mkdir('dest')
+            with open('demo.py', 'w') as f:
+                f.write('import os; os.chdir("dest")')
+
+            assert_python_ok(
+                '-m', self.profilermodule.__name__,
+                '-o', 'out.pstats',
+                'demo.py',
+            )
+
+            self.assertTrue(os.path.exists('out.pstats'))
+
 
 def regenerate_expected_output(filename, cls):
     filename = filename.rstrip('co')
index 10559de..4f78b99 100644 (file)
@@ -95,5 +95,9 @@ class StatsTestCase(unittest.TestCase):
         self.assertIn('pass2', funcs_called)
         self.assertIn('pass3', funcs_called)
 
+    def test_SortKey_enum(self):
+        self.assertEqual(SortKey.FILENAME, 'filename')
+        self.assertNotEqual(SortKey.FILENAME, SortKey.CALLS)
+
 if __name__ == "__main__":
     unittest.main()
index e56b337..e19af64 100644 (file)
@@ -1848,6 +1848,23 @@ class TestWhich(BaseTest, unittest.TestCase):
             rv = shutil.which(program, path=self.temp_dir)
             self.assertEqual(rv, temp_filexyz.name)
 
+    # Issue 40592: See https://bugs.python.org/issue40592
+    @unittest.skipUnless(sys.platform == "win32", 'test specific to Windows')
+    def test_pathext_with_empty_str(self):
+        ext = ".xyz"
+        temp_filexyz = tempfile.NamedTemporaryFile(dir=self.temp_dir,
+                                                   prefix="Tmp2", suffix=ext)
+        self.addCleanup(temp_filexyz.close)
+
+        # strip path and extension
+        program = os.path.basename(temp_filexyz.name)
+        program = os.path.splitext(program)[0]
+
+        with support.EnvironmentVarGuard() as env:
+            env['PATHEXT'] = f"{ext};"  # note the ;
+            rv = shutil.which(program, path=self.temp_dir)
+            self.assertEqual(rv, temp_filexyz.name)
+
 
 class TestWhichBytes(TestWhich):
     def setUp(self):
index 923f35e..ffba139 100644 (file)
@@ -500,8 +500,6 @@ class ImportSideEffectTests(unittest.TestCase):
 
     @test.support.requires_resource('network')
     @test.support.system_must_validate_cert
-    @unittest.skipUnless(sys.version_info[3] == 'final',
-                         'only for released versions')
     @unittest.skipUnless(hasattr(urllib.request, "HTTPSHandler"),
                          'need SSL support to download license')
     def test_license_exists_at_url(self):
@@ -509,6 +507,8 @@ class ImportSideEffectTests(unittest.TestCase):
         # string displayed by license in the absence of a LICENSE file.
         url = license._Printer__data.split()[1]
         req = urllib.request.Request(url, method='HEAD')
+        # Reset global urllib.request._opener
+        self.addCleanup(urllib.request.urlcleanup)
         try:
             with socket_helper.transient_internet(url):
                 with urllib.request.urlopen(req) as data:
index 5d496c6..27cb884 100644 (file)
@@ -3831,6 +3831,7 @@ class ThreadedTests(unittest.TestCase):
 
     @requires_minimum_version
     @requires_tls_version('TLSv1_2')
+    @requires_tls_version('TLSv1')
     def test_min_max_version_mismatch(self):
         client_context, server_context, hostname = testing_context()
         # client 1.0, server 1.2 (mismatch)
index ba2844d..7373fe2 100644 (file)
@@ -1826,6 +1826,10 @@ class POSIXProcessTestCase(BaseTestCase):
         with self.assertRaises(ValueError):
             subprocess.check_call(ZERO_RETURN_CMD, user=-1)
 
+        with self.assertRaises(OverflowError):
+            subprocess.check_call(ZERO_RETURN_CMD,
+                                  cwd=os.curdir, env=os.environ, user=2**64)
+
         if pwd is None and name_uid is not None:
             with self.assertRaises(ValueError):
                 subprocess.check_call(ZERO_RETURN_CMD, user=name_uid)
@@ -1869,6 +1873,10 @@ class POSIXProcessTestCase(BaseTestCase):
         with self.assertRaises(ValueError):
             subprocess.check_call(ZERO_RETURN_CMD, group=-1)
 
+        with self.assertRaises(OverflowError):
+            subprocess.check_call(ZERO_RETURN_CMD,
+                                  cwd=os.curdir, env=os.environ, group=2**64)
+
         if grp is None:
             with self.assertRaises(ValueError):
                 subprocess.check_call(ZERO_RETURN_CMD, group=name_group)
@@ -1917,6 +1925,11 @@ class POSIXProcessTestCase(BaseTestCase):
         with self.assertRaises(ValueError):
             subprocess.check_call(ZERO_RETURN_CMD, extra_groups=[-1])
 
+        with self.assertRaises(ValueError):
+            subprocess.check_call(ZERO_RETURN_CMD,
+                                  cwd=os.curdir, env=os.environ,
+                                  extra_groups=[2**64])
+
         if grp is None:
             with self.assertRaises(ValueError):
                 subprocess.check_call(ZERO_RETURN_CMD,
@@ -3147,6 +3160,19 @@ class POSIXProcessTestCase(BaseTestCase):
         # so Popen failed to read it and uses a default returncode instead.
         self.assertIsNotNone(proc.returncode)
 
+    def test_send_signal_race2(self):
+        # bpo-40550: the process might exist between the returncode check and
+        # the kill operation
+        p = subprocess.Popen([sys.executable, '-c', 'exit(1)'])
+
+        # wait for process to exit
+        while not p.returncode:
+            p.poll()
+
+        with mock.patch.object(p, 'poll', new=lambda: None):
+            p.returncode = None
+            p.send_signal(signal.SIGTERM)
+
     def test_communicate_repeated_call_after_stdout_close(self):
         proc = subprocess.Popen([sys.executable, '-c',
                                  'import os, time; os.close(1), time.sleep(2)'],
index d193558..6585071 100644 (file)
@@ -11,6 +11,8 @@ import sys
 
 glob = 42
 some_var = 12
+some_non_assigned_global_var = 11
+some_assigned_global_var = 11
 
 class Mine:
     instance_var = 24
@@ -19,6 +21,8 @@ class Mine:
 
 def spam(a, b, *var, **kw):
     global bar
+    global some_assigned_global_var
+    some_assigned_global_var = 12
     bar = 47
     some_var = 10
     x = 23
@@ -81,14 +85,14 @@ class SymtableTest(unittest.TestCase):
 
     def test_lineno(self):
         self.assertEqual(self.top.get_lineno(), 0)
-        self.assertEqual(self.spam.get_lineno(), 12)
+        self.assertEqual(self.spam.get_lineno(), 14)
 
     def test_function_info(self):
         func = self.spam
         self.assertEqual(sorted(func.get_parameters()), ["a", "b", "kw", "var"])
         expected = ['a', 'b', 'internal', 'kw', 'other_internal', 'some_var', 'var', 'x']
         self.assertEqual(sorted(func.get_locals()), expected)
-        self.assertEqual(sorted(func.get_globals()), ["bar", "glob"])
+        self.assertEqual(sorted(func.get_globals()), ["bar", "glob", "some_assigned_global_var"])
         self.assertEqual(self.internal.get_frees(), ("x",))
 
     def test_globals(self):
@@ -99,6 +103,9 @@ class SymtableTest(unittest.TestCase):
         self.assertFalse(self.internal.lookup("x").is_global())
         self.assertFalse(self.Mine.lookup("instance_var").is_global())
         self.assertTrue(self.spam.lookup("bar").is_global())
+        # Module-scope globals are both global and local
+        self.assertTrue(self.top.lookup("some_non_assigned_global_var").is_global())
+        self.assertTrue(self.top.lookup("some_assigned_global_var").is_global())
 
     def test_nonlocal(self):
         self.assertFalse(self.spam.lookup("some_var").is_nonlocal())
@@ -109,6 +116,9 @@ class SymtableTest(unittest.TestCase):
     def test_local(self):
         self.assertTrue(self.spam.lookup("x").is_local())
         self.assertFalse(self.spam.lookup("bar").is_local())
+        # Module-scope globals are both global and local
+        self.assertTrue(self.top.lookup("some_non_assigned_global_var").is_local())
+        self.assertTrue(self.top.lookup("some_assigned_global_var").is_local())
 
     def test_free(self):
         self.assertTrue(self.internal.lookup("x").is_free())
@@ -227,6 +237,10 @@ class SymtableTest(unittest.TestCase):
         top = symtable.symtable(code, "?", "exec")
         self.assertIsNotNone(find_block(top, "\u017d"))
 
+    def test_symtable_repr(self):
+        self.assertEqual(str(self.top), "<SymbolTable for module ?>")
+        self.assertEqual(str(self.spam), "<Function SymbolTable for spam in ?>")
+
 
 if __name__ == '__main__':
     unittest.main()
index d6583b9..f0c9c98 100644 (file)
@@ -802,6 +802,9 @@ class SyntaxTestCase(unittest.TestCase):
         else:
             self.fail("compile() did not raise SyntaxError")
 
+    def test_curly_brace_after_primary_raises_immediately(self):
+        self._check_error("f{", "invalid syntax", mode="single")
+
     def test_assign_call(self):
         self._check_error("f() = 1", "assign")
 
@@ -943,6 +946,40 @@ pass
         except SyntaxError:
             self.fail("Empty line after a line continuation character is valid.")
 
+    @support.cpython_only
+    def test_nested_named_except_blocks(self):
+        code = ""
+        for i in range(12):
+            code += f"{'    '*i}try:\n"
+            code += f"{'    '*(i+1)}raise Exception\n"
+            code += f"{'    '*i}except Exception as e:\n"
+        code += f"{' '*4*12}pass"
+        self._check_error(code, "too many statically nested blocks")
+
+    def test_barry_as_flufl_with_syntax_errors(self):
+        # The "barry_as_flufl" rule can produce some "bugs-at-a-distance" if
+        # is reading the wrong token in the presence of syntax errors later
+        # in the file. See bpo-42214 for more information.
+        code = """
+def func1():
+    if a != b:
+        raise ValueError
+
+def func2():
+    try
+        return 1
+    finally:
+        pass
+"""
+        self._check_error(code, "invalid syntax")
+
+    def test_invalid_line_continuation_left_recursive(self):
+        # Check bpo-42218: SyntaxErrors following left-recursive rules
+        # (t_primary_raw in this case) need to be tested explicitly
+        self._check_error("A.\u018a\\ ",
+                          "unexpected character after line continuation character")
+        self._check_error("A.\u03bc\\\n",
+                          "unexpected EOF while parsing")
 
 def test_main():
     support.run_unittest(SyntaxTestCase)
index 3ddeb97..29cde91 100644 (file)
@@ -1346,10 +1346,10 @@ class WriteTest(WriteTestBase, unittest.TestCase):
                 f.write('something\n')
             os.symlink(source_file, target_file)
             with tarfile.open(temparchive, 'w') as tar:
-                tar.add(source_file)
-                tar.add(target_file)
+                tar.add(source_file, arcname="source")
+                tar.add(target_file, arcname="symlink")
             # Let's extract it to the location which contains the symlink
-            with tarfile.open(temparchive) as tar:
+            with tarfile.open(temparchive, errorlevel=2) as tar:
                 # this should not raise OSError: [Errno 17] File exists
                 try:
                     tar.extractall(path=tempdir)
@@ -1416,12 +1416,15 @@ class WriteTest(WriteTestBase, unittest.TestCase):
                                    pax_headers={'non': 'empty'})
             self.assertFalse(f.closed)
 
+
 class GzipWriteTest(GzipTest, WriteTest):
     pass
 
+
 class Bz2WriteTest(Bz2Test, WriteTest):
     pass
 
+
 class LzmaWriteTest(LzmaTest, WriteTest):
     pass
 
@@ -1464,8 +1467,17 @@ class StreamWriteTest(WriteTestBase, unittest.TestCase):
         finally:
             os.umask(original_umask)
 
+
 class GzipStreamWriteTest(GzipTest, StreamWriteTest):
-    pass
+    def test_source_directory_not_leaked(self):
+        """
+        Ensure the source directory is not included in the tar header
+        per bpo-41316.
+        """
+        tarfile.open(tmpname, self.mode).close()
+        payload = pathlib.Path(tmpname).read_text(encoding='latin-1')
+        assert os.path.dirname(tmpname) not in payload
+
 
 class Bz2StreamWriteTest(Bz2Test, StreamWriteTest):
     decompressor = bz2.BZ2Decompressor if bz2 else None
index 81e5f70..c21cdf8 100644 (file)
@@ -439,6 +439,35 @@ class ThreadTests(BaseTestCase):
         t = threading.Thread(daemon=True)
         self.assertTrue(t.daemon)
 
+    @unittest.skipUnless(hasattr(os, 'fork'), 'needs os.fork()')
+    def test_fork_at_exit(self):
+        # bpo-42350: Calling os.fork() after threading._shutdown() must
+        # not log an error.
+        code = textwrap.dedent("""
+            import atexit
+            import os
+            import sys
+            from test.support import wait_process
+
+            # Import the threading module to register its "at fork" callback
+            import threading
+
+            def exit_handler():
+                pid = os.fork()
+                if not pid:
+                    print("child process ok", file=sys.stderr, flush=True)
+                    # child process
+                    sys.exit()
+                else:
+                    wait_process(pid, exitcode=0)
+
+            # exit_handler() will be called after threading._shutdown()
+            atexit.register(exit_handler)
+        """)
+        _, out, err = assert_python_ok("-c", code)
+        self.assertEqual(out, b'')
+        self.assertEqual(err.rstrip(), b'child process ok')
+
     @unittest.skipUnless(hasattr(os, 'fork'), 'test needs fork()')
     def test_dummy_thread_after_fork(self):
         # Issue #14308: a dummy thread in the active list doesn't mess up
index 80e43fa..6674edc 100644 (file)
@@ -1040,6 +1040,36 @@ class TestOldPyTime(CPyTimeTestCase, unittest.TestCase):
             with self.assertRaises(ValueError):
                 pytime_object_to_timespec(float('nan'), time_rnd)
 
+@unittest.skipUnless(sys.platform == "darwin", "test weak linking on macOS")
+class TestTimeWeaklinking(unittest.TestCase):
+    # These test cases verify that weak linking support on macOS works
+    # as expected. These cases only test new behaviour introduced by weak linking,
+    # regular behaviour is tested by the normal test cases.
+    #
+    # See the section on Weak Linking in Mac/README.txt for more information.
+    def test_clock_functions(self):
+        import sysconfig
+        import platform
+
+        config_vars = sysconfig.get_config_vars()
+        var_name = "HAVE_CLOCK_GETTIME"
+        if var_name not in config_vars or not config_vars[var_name]:
+            raise unittest.SkipTest(f"{var_name} is not available")
+
+        mac_ver = tuple(int(x) for x in platform.mac_ver()[0].split("."))
+
+        clock_names = [
+            "CLOCK_MONOTONIC", "clock_gettime", "clock_gettime_ns", "clock_settime",
+            "clock_settime_ns", "clock_getres"]
+
+        if mac_ver >= (10, 12):
+            for name in clock_names:
+                self.assertTrue(hasattr(time, name), f"time.{name} is not available")
+
+        else:
+            for name in clock_names:
+                self.assertFalse(hasattr(time, name), f"time.{name} is available")
+
 
 if __name__ == "__main__":
     unittest.main()
index f9a5f2f..8549ba2 100644 (file)
@@ -1103,7 +1103,19 @@ class TestTracebackException(unittest.TestCase):
         self.assertEqual(exc_info[0], exc.exc_type)
         self.assertEqual(str(exc_info[1]), str(exc))
 
-    def test_comparison(self):
+    def test_no_refs_to_exception_and_traceback_objects(self):
+        try:
+            1/0
+        except Exception:
+            exc_info = sys.exc_info()
+
+        refcnt1 = sys.getrefcount(exc_info[1])
+        refcnt2 = sys.getrefcount(exc_info[2])
+        exc = traceback.TracebackException(*exc_info)
+        self.assertEqual(sys.getrefcount(exc_info[1]), refcnt1)
+        self.assertEqual(sys.getrefcount(exc_info[2]), refcnt2)
+
+    def test_comparison_basic(self):
         try:
             1/0
         except Exception:
@@ -1115,6 +1127,53 @@ class TestTracebackException(unittest.TestCase):
         self.assertNotEqual(exc, object())
         self.assertEqual(exc, ALWAYS_EQ)
 
+    def test_comparison_params_variations(self):
+        def raise_exc():
+            try:
+                raise ValueError('bad value')
+            except:
+                raise
+
+        def raise_with_locals():
+            x, y = 1, 2
+            raise_exc()
+
+        try:
+            raise_with_locals()
+        except Exception:
+            exc_info = sys.exc_info()
+
+        exc = traceback.TracebackException(*exc_info)
+        exc1 = traceback.TracebackException(*exc_info, limit=10)
+        exc2 = traceback.TracebackException(*exc_info, limit=2)
+
+        self.assertEqual(exc, exc1)      # limit=10 gets all frames
+        self.assertNotEqual(exc, exc2)   # limit=2 truncates the output
+
+        # locals change the output
+        exc3 = traceback.TracebackException(*exc_info, capture_locals=True)
+        self.assertNotEqual(exc, exc3)
+
+        # there are no locals in the innermost frame
+        exc4 = traceback.TracebackException(*exc_info, limit=-1)
+        exc5 = traceback.TracebackException(*exc_info, limit=-1, capture_locals=True)
+        self.assertEqual(exc4, exc5)
+
+        # there are locals in the next-to-innermost frame
+        exc6 = traceback.TracebackException(*exc_info, limit=-2)
+        exc7 = traceback.TracebackException(*exc_info, limit=-2, capture_locals=True)
+        self.assertNotEqual(exc6, exc7)
+
+    def test_comparison_equivalent_exceptions_are_equal(self):
+        excs = []
+        for _ in range(2):
+            try:
+                1/0
+            except:
+                excs.append(traceback.TracebackException(*sys.exc_info()))
+        self.assertEqual(excs[0], excs[1])
+        self.assertEqual(list(excs[0].format()), list(excs[1].format()))
+
     def test_unhashable(self):
         class UnhashableException(Exception):
             def __eq__(self, other):
@@ -1156,7 +1215,7 @@ class TestTracebackException(unittest.TestCase):
         f = test_frame(c, None, None)
         tb = test_tb(f, 6, None)
         exc = traceback.TracebackException(Exception, e, tb, lookup_lines=False)
-        self.assertEqual({}, linecache.cache)
+        self.assertEqual(linecache.cache, {})
         linecache.updatecache('/foo.py', globals())
         self.assertEqual(exc.stack[0].line, "import sys")
 
index 7f96aff..13cf20e 100644 (file)
@@ -300,6 +300,8 @@ class UnionTests(BaseTestCase):
         self.assertEqual(repr(u), repr(int))
         u = Union[List[int], int]
         self.assertEqual(repr(u), 'typing.Union[typing.List[int], int]')
+        u = Union[list[int], dict[str, float]]
+        self.assertEqual(repr(u), 'typing.Union[list[int], dict[str, float]]')
 
     def test_cannot_subclass(self):
         with self.assertRaises(TypeError):
@@ -411,6 +413,7 @@ class TupleTests(BaseTestCase):
         self.assertEqual(repr(Tuple[()]), 'typing.Tuple[()]')
         self.assertEqual(repr(Tuple[int, float]), 'typing.Tuple[int, float]')
         self.assertEqual(repr(Tuple[int, ...]), 'typing.Tuple[int, ...]')
+        self.assertEqual(repr(Tuple[list[int]]), 'typing.Tuple[list[int]]')
 
     def test_errors(self):
         with self.assertRaises(TypeError):
@@ -483,6 +486,8 @@ class CallableTests(BaseTestCase):
         self.assertEqual(repr(ct2), 'typing.Callable[[str, float], int]')
         ctv = Callable[..., str]
         self.assertEqual(repr(ctv), 'typing.Callable[..., str]')
+        ct3 = Callable[[str, float], list[int]]
+        self.assertEqual(repr(ct3), 'typing.Callable[[str, float], list[int]]')
 
     def test_callable_with_ellipsis(self):
 
@@ -527,6 +532,7 @@ class LiteralTests(BaseTestCase):
         self.assertEqual(repr(Literal[int]), "typing.Literal[int]")
         self.assertEqual(repr(Literal), "typing.Literal")
         self.assertEqual(repr(Literal[None]), "typing.Literal[None]")
+        self.assertEqual(repr(Literal[1, 2, 3, 3]), "typing.Literal[1, 2, 3]")
 
     def test_cannot_init(self):
         with self.assertRaises(TypeError):
@@ -558,6 +564,35 @@ class LiteralTests(BaseTestCase):
         with self.assertRaises(TypeError):
             Literal[1][1]
 
+    def test_equal(self):
+        self.assertNotEqual(Literal[0], Literal[False])
+        self.assertNotEqual(Literal[True], Literal[1])
+        self.assertNotEqual(Literal[1], Literal[2])
+        self.assertNotEqual(Literal[1, True], Literal[1])
+        self.assertEqual(Literal[1], Literal[1])
+        self.assertEqual(Literal[1, 2], Literal[2, 1])
+        self.assertEqual(Literal[1, 2, 3], Literal[1, 2, 3, 3])
+
+    def test_hash(self):
+        self.assertEqual(hash(Literal[1]), hash(Literal[1]))
+        self.assertEqual(hash(Literal[1, 2]), hash(Literal[2, 1]))
+        self.assertEqual(hash(Literal[1, 2, 3]), hash(Literal[1, 2, 3, 3]))
+
+    def test_args(self):
+        self.assertEqual(Literal[1, 2, 3].__args__, (1, 2, 3))
+        self.assertEqual(Literal[1, 2, 3, 3].__args__, (1, 2, 3))
+        self.assertEqual(Literal[1, Literal[2], Literal[3, 4]].__args__, (1, 2, 3, 4))
+        # Mutable arguments will not be deduplicated
+        self.assertEqual(Literal[[], []].__args__, ([], []))
+
+    def test_flatten(self):
+        l1 = Literal[Literal[1], Literal[2], Literal[3]]
+        l2 = Literal[Literal[1, 2], 3]
+        l3 = Literal[Literal[1, 2, 3]]
+        for l in l1, l2, l3:
+            self.assertEqual(l, Literal[1, 2, 3])
+            self.assertEqual(l.__args__, (1, 2, 3))
+
 
 XK = TypeVar('XK', str, bytes)
 XV = TypeVar('XV')
@@ -2273,6 +2308,8 @@ class FinalTests(BaseTestCase):
         self.assertEqual(repr(cv), 'typing.Final[int]')
         cv = Final[Employee]
         self.assertEqual(repr(cv), 'typing.Final[%s.Employee]' % __name__)
+        cv = Final[tuple[int]]
+        self.assertEqual(repr(cv), 'typing.Final[tuple[int]]')
 
     def test_cannot_subclass(self):
         with self.assertRaises(TypeError):
index 2ee4e64..23508c5 100644 (file)
@@ -2523,11 +2523,13 @@ class CAPITest(unittest.TestCase):
     def test_from_format(self):
         support.import_module('ctypes')
         from ctypes import (
+            c_char_p,
             pythonapi, py_object, sizeof,
             c_int, c_long, c_longlong, c_ssize_t,
             c_uint, c_ulong, c_ulonglong, c_size_t, c_void_p)
         name = "PyUnicode_FromFormat"
         _PyUnicode_FromFormat = getattr(pythonapi, name)
+        _PyUnicode_FromFormat.argtypes = (c_char_p,)
         _PyUnicode_FromFormat.restype = py_object
 
         def PyUnicode_FromFormat(format, *args):
index 532aa3a..c7c8613 100644 (file)
@@ -152,6 +152,18 @@ class UnparseTestCase(ASTTestCase):
         # See issue 25180
         self.check_ast_roundtrip(r"""f'{f"{0}"*3}'""")
         self.check_ast_roundtrip(r"""f'{f"{y}"*3}'""")
+        self.check_ast_roundtrip("""f''""")
+        self.check_ast_roundtrip('''f"""'end' "quote\\""""''')
+
+    def test_fstrings_complicated(self):
+        # See issue 28002
+        self.check_ast_roundtrip("""f'''{"'"}'''""")
+        self.check_ast_roundtrip('''f\'\'\'-{f"""*{f"+{f'.{x}.'}+"}*"""}-\'\'\'''')
+        self.check_ast_roundtrip('''f\'\'\'-{f"""*{f"+{f'.{x}.'}+"}*"""}-'single quote\\'\'\'\'''')
+        self.check_ast_roundtrip('f"""{\'\'\'\n\'\'\'}"""')
+        self.check_ast_roundtrip('f"""{g(\'\'\'\n\'\'\')}"""')
+        self.check_ast_roundtrip('''f"a\\r\\nb"''')
+        self.check_ast_roundtrip('''f"\\u2028{'x'}"''')
 
     def test_strings(self):
         self.check_ast_roundtrip("u'foo'")
@@ -311,6 +323,9 @@ class UnparseTestCase(ASTTestCase):
             )
         )
 
+    def test_invalid_fstring_backslash(self):
+        self.check_invalid(ast.FormattedValue(value=ast.Constant(value="\\\\")))
+
     def test_invalid_set(self):
         self.check_invalid(ast.Set(elts=[]))
 
@@ -330,8 +345,8 @@ class UnparseTestCase(ASTTestCase):
             '\r\\r\t\\t\n\\n',
             '""">>> content = \"\"\"blabla\"\"\" <<<"""',
             r'foo\n\x00',
-            '🐍⛎𩸽üéş^\N{LONG RIGHTWARDS SQUIGGLE ARROW}'
-
+            "' \\'\\'\\'\"\"\" \"\"\\'\\' \\'",
+            '🐍⛎𩸽üéş^\\\\X\\\\BB\N{LONG RIGHTWARDS SQUIGGLE ARROW}'
         )
         for docstring in docstrings:
             # check as Module docstrings for easy testing
@@ -416,7 +431,6 @@ class CosmeticTestCase(ASTTestCase):
         self.check_src_roundtrip("call((yield x))")
         self.check_src_roundtrip("return x + (yield x)")
 
-
     def test_class_bases_and_keywords(self):
         self.check_src_roundtrip("class X:\n    pass")
         self.check_src_roundtrip("class X(A):\n    pass")
@@ -429,6 +443,13 @@ class CosmeticTestCase(ASTTestCase):
         self.check_src_roundtrip("class X(*args):\n    pass")
         self.check_src_roundtrip("class X(*args, **kwargs):\n    pass")
 
+    def test_fstrings(self):
+        self.check_src_roundtrip('''f\'\'\'-{f"""*{f"+{f'.{x}.'}+"}*"""}-\'\'\'''')
+        self.check_src_roundtrip('''f"\\u2028{'x'}"''')
+        self.check_src_roundtrip(r"f'{x}\n'")
+        self.check_src_roundtrip('''f''\'{"""\n"""}\\n''\'''')
+        self.check_src_roundtrip('''f''\'{f"""{x}\n"""}\\n''\'''')
+
     def test_docstrings(self):
         docstrings = (
             '"""simple doc string"""',
@@ -443,6 +464,10 @@ class CosmeticTestCase(ASTTestCase):
             '""""""',
             '"""\'\'\'"""',
             '"""\'\'\'\'\'\'"""',
+            '"""🐍⛎𩸽üéş^\\\\X\\\\BB⟿"""',
+            '"""end in single \'quote\'"""',
+            "'''end in double \"quote\"'''",
+            '"""almost end in double "quote"."""',
         )
 
         for prefix in docstring_prefixes:
@@ -483,9 +508,8 @@ class DirectoryTestCase(ASTTestCase):
 
     lib_dir = pathlib.Path(__file__).parent / ".."
     test_directories = (lib_dir, lib_dir / "test")
-    skip_files = {"test_fstring.py"}
     run_always_files = {"test_grammar.py", "test_syntax.py", "test_compile.py",
-                        "test_ast.py", "test_asdl_parser.py"}
+                        "test_ast.py", "test_asdl_parser.py", "test_fstring.py"}
 
     _files_to_test = None
 
@@ -525,14 +549,6 @@ class DirectoryTestCase(ASTTestCase):
             if test.support.verbose:
                 print(f"Testing {item.absolute()}")
 
-            # Some f-strings are not correctly round-tripped by
-            # Tools/parser/unparse.py.  See issue 28002 for details.
-            # We need to skip files that contain such f-strings.
-            if item.name in self.skip_files:
-                if test.support.verbose:
-                    print(f"Skipping {item.absolute()}: see issue 28002")
-                continue
-
             with self.subTest(filename=item):
                 source = read_pyfile(item)
                 self.check_ast_roundtrip(source)
index cbfa9ba..b71be54 100644 (file)
@@ -1447,6 +1447,18 @@ class HandlerTests(unittest.TestCase):
         bypass = {'exclude_simple': True, 'exceptions': []}
         self.assertTrue(_proxy_bypass_macosx_sysconf('test', bypass))
 
+        # Check that invalid prefix lengths are ignored
+        bypass = {
+            'exclude_simple': False,
+            'exceptions': [ '10.0.0.0/40', '172.19.10.0/24' ]
+        }
+        host = '172.19.10.5'
+        self.assertTrue(_proxy_bypass_macosx_sysconf(host, bypass),
+                        'expected bypass of %s to be True' % host)
+        host = '10.0.1.5'
+        self.assertFalse(_proxy_bypass_macosx_sysconf(host, bypass),
+                        'expected bypass of %s to be False' % host)
+
     def check_basic_auth(self, headers, realm):
         with self.subTest(realm=realm, headers=headers):
             opener = OpenerDirector()
index b1d7a15..f21bf65 100644 (file)
@@ -7,7 +7,7 @@ __email__ = "mbland at acm dot org"
 import sys
 import unittest
 from collections import deque
-from contextlib import _GeneratorContextManager, contextmanager
+from contextlib import _GeneratorContextManager, contextmanager, nullcontext
 
 
 class MockContextManager(_GeneratorContextManager):
@@ -641,6 +641,12 @@ class AssignmentTargetTestCase(unittest.TestCase):
             self.assertEqual(blah.two, 2)
             self.assertEqual(blah.three, 3)
 
+    def testWithExtendedTargets(self):
+        with nullcontext(range(1, 5)) as (a, *b, c):
+            self.assertEqual(a, 1)
+            self.assertEqual(b, [2, 3])
+            self.assertEqual(c, 4)
+
 
 class ExitSwallowsExceptionTestCase(unittest.TestCase):
 
index d01649d..5632b8b 100644 (file)
@@ -3894,6 +3894,14 @@ class C14NTest(unittest.TestCase):
         #self.assertEqual(c14n_roundtrip("<doc xmlns:x='http://example.com/x' xmlns='http://example.com/default'><b y:a1='1' xmlns='http://example.com/default' a3='3' xmlns:y='http://example.com/y' y:a2='2'/></doc>"),
         #'<doc xmlns:x="http://example.com/x"><b xmlns:y="http://example.com/y" a3="3" y:a1="1" y:a2="2"></b></doc>')
 
+        # Namespace issues
+        xml = '<X xmlns="http://nps/a"><Y targets="abc,xyz"></Y></X>'
+        self.assertEqual(c14n_roundtrip(xml), xml)
+        xml = '<X xmlns="http://nps/a"><Y xmlns="http://nsp/b" targets="abc,xyz"></Y></X>'
+        self.assertEqual(c14n_roundtrip(xml), xml)
+        xml = '<X xmlns="http://nps/a"><Y xmlns:b="http://nsp/b" b:targets="abc,xyz"></Y></X>'
+        self.assertEqual(c14n_roundtrip(xml), xml)
+
     def test_c14n_exclusion(self):
         xml = textwrap.dedent("""\
         <root xmlns:x="http://example.com/x">
index b7bc218..999197a 100644 (file)
@@ -1855,11 +1855,14 @@ class OtherTests(unittest.TestCase):
             self.assertEqual(zipf.comment, b"an updated comment")
 
         # check that comments are correctly shortened in append mode
+        # and the file is indeed truncated
         with zipfile.ZipFile(TESTFN,mode="w") as zipf:
             zipf.comment = b"original comment that's longer"
             zipf.writestr("foo.txt", "O, for a Muse of Fire!")
+        original_zip_size = os.path.getsize(TESTFN)
         with zipfile.ZipFile(TESTFN,mode="a") as zipf:
             zipf.comment = b"shorter comment"
+        self.assertTrue(original_zip_size > os.path.getsize(TESTFN))
         with zipfile.ZipFile(TESTFN,mode="r") as zipf:
             self.assertEqual(zipf.comment, b"shorter comment")
 
index 8570326..d4704b7 100644 (file)
@@ -6,7 +6,6 @@ import dataclasses
 import importlib.metadata
 import io
 import json
-import lzma
 import os
 import pathlib
 import pickle
@@ -20,7 +19,9 @@ from functools import cached_property
 
 from . import _support as test_support
 from ._support import OS_ENV_LOCK, TZPATH_TEST_LOCK, ZoneInfoTestBase
+from test.support import import_module
 
+lzma = import_module('lzma')
 py_zoneinfo, c_zoneinfo = test_support.get_modules()
 
 try:
index ab29db7..d96d99a 100644 (file)
@@ -826,8 +826,12 @@ class Thread:
         # they may be in an invalid state leading to a deadlock or crash.
         self._started._at_fork_reinit()
         if is_alive:
-            self._tstate_lock._at_fork_reinit()
-            self._tstate_lock.acquire()
+            # bpo-42350: If the fork happens when the thread is already stopped
+            # (ex: after threading._shutdown() has been called), _tstate_lock
+            # is None. Do nothing in this case.
+            if self._tstate_lock is not None:
+                self._tstate_lock._at_fork_reinit()
+                self._tstate_lock.acquire()
         else:
             # The thread isn't alive after fork: it doesn't have a tstate
             # anymore.
index 1067ab6..2175afc 100644 (file)
@@ -146,10 +146,10 @@ def _splitdict(tk, v, cut_minus=True, conv=None):
 
 class EventType(str, enum.Enum):
     KeyPress = '2'
-    Key = KeyPress,
+    Key = KeyPress
     KeyRelease = '3'
     ButtonPress = '4'
-    Button = ButtonPress,
+    Button = ButtonPress
     ButtonRelease = '5'
     Motion = '6'
     Enter = '7'
@@ -180,13 +180,12 @@ class EventType(str, enum.Enum):
     Colormap = '32'
     ClientMessage = '33'    # undocumented
     Mapping = '34'          # undocumented
-    VirtualEvent = '35',    # undocumented
-    Activate = '36',
-    Deactivate = '37',
-    MouseWheel = '38',
+    VirtualEvent = '35'     # undocumented
+    Activate = '36'
+    Deactivate = '37'
+    MouseWheel = '38'
 
-    def __str__(self):
-        return self.name
+    __str__ = str.__str__
 
 
 class Event:
@@ -266,7 +265,7 @@ class Event:
                 'num', 'delta', 'focus',
                 'x', 'y', 'width', 'height')
         return '<%s event%s>' % (
-            self.type,
+            getattr(self.type, 'name', self.type),
             ''.join(' %s=%s' % (k, attrs[k]) for k in keys if k in attrs)
         )
 
index 1e08974..b8eea25 100644 (file)
@@ -192,6 +192,54 @@ class MiscTest(AbstractTkTest, unittest.TestCase):
         with self.assertRaises(tkinter.TclError):
             root.clipboard_get()
 
+    def test_event_repr_defaults(self):
+        e = tkinter.Event()
+        e.serial = 12345
+        e.num = '??'
+        e.height = '??'
+        e.keycode = '??'
+        e.state = 0
+        e.time = 123456789
+        e.width = '??'
+        e.x = '??'
+        e.y = '??'
+        e.char = ''
+        e.keysym = '??'
+        e.keysym_num = '??'
+        e.type = '100'
+        e.widget = '??'
+        e.x_root = '??'
+        e.y_root = '??'
+        e.delta = 0
+        self.assertEqual(repr(e), '<100 event>')
+
+    def test_event_repr(self):
+        e = tkinter.Event()
+        e.serial = 12345
+        e.num = 3
+        e.focus = True
+        e.height = 200
+        e.keycode = 65
+        e.state = 0x30405
+        e.time = 123456789
+        e.width = 300
+        e.x = 10
+        e.y = 20
+        e.char = 'A'
+        e.send_event = True
+        e.keysym = 'Key-A'
+        e.keysym_num = ord('A')
+        e.type = tkinter.EventType.Configure
+        e.widget = '.text'
+        e.x_root = 1010
+        e.y_root = 1020
+        e.delta = -1
+        self.assertEqual(repr(e),
+                         "<Configure event send_event=True"
+                         " state=Shift|Control|Button3|0x30000"
+                         " keysym=Key-A keycode=65 char='A'"
+                         " num=3 delta=-1 focus=True"
+                         " x=10 y=20 width=300 height=200>")
 
 tests_gui = (MiscTest, )
 
index 721e813..b6f792d 100644 (file)
@@ -940,7 +940,8 @@ class ScaleTest(AbstractWidgetTest, unittest.TestCase):
 
     def test_from(self):
         widget = self.create()
-        self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=float_round)
+        conv = False if get_tk_patchlevel() >= (8, 6, 10) else float_round
+        self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=conv)
 
     def test_label(self):
         widget = self.create()
index a45f882..6937ba1 100644 (file)
@@ -114,7 +114,6 @@ class LabeledScaleTest(AbstractTkTest, unittest.TestCase):
     def test_horizontal_range(self):
         lscale = ttk.LabeledScale(self.root, from_=0, to=10)
         lscale.pack()
-        lscale.wait_visibility()
         lscale.update()
 
         linfo_1 = lscale.label.place_info()
@@ -144,7 +143,6 @@ class LabeledScaleTest(AbstractTkTest, unittest.TestCase):
     def test_variable_change(self):
         x = ttk.LabeledScale(self.root)
         x.pack()
-        x.wait_visibility()
         x.update()
 
         curr_xcoord = x.scale.coords()[0]
@@ -187,7 +185,6 @@ class LabeledScaleTest(AbstractTkTest, unittest.TestCase):
     def test_resize(self):
         x = ttk.LabeledScale(self.root)
         x.pack(expand=True, fill='both')
-        x.wait_visibility()
         x.update()
 
         width, height = x.master.winfo_width(), x.master.winfo_height()
@@ -268,7 +265,6 @@ class OptionMenuTest(AbstractTkTest, unittest.TestCase):
 
         # check that variable is updated correctly
         optmenu.pack()
-        optmenu.wait_visibility()
         optmenu['menu'].invoke(0)
         self.assertEqual(optmenu._variable.get(), items[0])
 
@@ -299,9 +295,7 @@ class OptionMenuTest(AbstractTkTest, unittest.TestCase):
         textvar2 = tkinter.StringVar(self.root)
         optmenu2 = ttk.OptionMenu(self.root, textvar2, default, *items)
         optmenu.pack()
-        optmenu.wait_visibility()
         optmenu2.pack()
-        optmenu2.wait_visibility()
         optmenu['menu'].invoke(1)
         optmenu2['menu'].invoke(2)
         optmenu_stringvar_name = optmenu['menu'].entrycget(0, 'variable')
index f8e69a9..5c23d6f 100644 (file)
@@ -137,6 +137,9 @@ class InternalFunctionsTest(unittest.TestCase):
         result = ttk._format_mapdict(opts)
         self.assertEqual(result, ('-üñíćódè', 'á vãl'))
 
+        self.assertEqual(ttk._format_mapdict({'opt': [('value',)]}),
+                         ('-opt', '{} value'))
+
         # empty states
         valid = {'opt': [('', '', 'hi')]}
         self.assertEqual(ttk._format_mapdict(valid), ('-opt', '{ } hi'))
@@ -159,10 +162,6 @@ class InternalFunctionsTest(unittest.TestCase):
         opts = {'a': None}
         self.assertRaises(TypeError, ttk._format_mapdict, opts)
 
-        # items in the value must have size >= 2
-        self.assertRaises(IndexError, ttk._format_mapdict,
-            {'a': [('invalid', )]})
-
 
     def test_format_elemcreate(self):
         self.assertTrue(ttk._format_elemcreate(None), (None, ()))
index 3537536..38d70d7 100644 (file)
@@ -1,11 +1,23 @@
 import unittest
+import sys
 import tkinter
 from tkinter import ttk
+from test import support
 from test.support import requires, run_unittest
 from tkinter.test.support import AbstractTkTest
 
 requires('gui')
 
+CLASS_NAMES = [
+    '.', 'ComboboxPopdownFrame', 'Heading',
+    'Horizontal.TProgressbar', 'Horizontal.TScale', 'Item', 'Sash',
+    'TButton', 'TCheckbutton', 'TCombobox', 'TEntry',
+    'TLabelframe', 'TLabelframe.Label', 'TMenubutton',
+    'TNotebook', 'TNotebook.Tab', 'Toolbutton', 'TProgressbar',
+    'TRadiobutton', 'Treeview', 'TScale', 'TScrollbar', 'TSpinbox',
+    'Vertical.TProgressbar', 'Vertical.TScale'
+]
+
 class StyleTest(AbstractTkTest, unittest.TestCase):
 
     def setUp(self):
@@ -23,11 +35,36 @@ class StyleTest(AbstractTkTest, unittest.TestCase):
 
     def test_map(self):
         style = self.style
-        style.map('TButton', background=[('active', 'background', 'blue')])
-        self.assertEqual(style.map('TButton', 'background'),
-            [('active', 'background', 'blue')] if self.wantobjects else
-            [('active background', 'blue')])
-        self.assertIsInstance(style.map('TButton'), dict)
+
+        # Single state
+        for states in ['active'], [('active',)]:
+            with self.subTest(states=states):
+                style.map('TButton', background=[(*states, 'white')])
+                expected = [('active', 'white')]
+                self.assertEqual(style.map('TButton', 'background'), expected)
+                m = style.map('TButton')
+                self.assertIsInstance(m, dict)
+                self.assertEqual(m['background'], expected)
+
+        # Multiple states
+        for states in ['pressed', '!disabled'], ['pressed !disabled'], [('pressed', '!disabled')]:
+            with self.subTest(states=states):
+                style.map('TButton', background=[(*states, 'black')])
+                expected = [('pressed', '!disabled', 'black')]
+                self.assertEqual(style.map('TButton', 'background'), expected)
+                m = style.map('TButton')
+                self.assertIsInstance(m, dict)
+                self.assertEqual(m['background'], expected)
+
+        # Default state
+        for states in [], [''], [()]:
+            with self.subTest(states=states):
+                style.map('TButton', background=[(*states, 'grey')])
+                expected = [('grey',)]
+                self.assertEqual(style.map('TButton', 'background'), expected)
+                m = style.map('TButton')
+                self.assertIsInstance(m, dict)
+                self.assertEqual(m['background'], expected)
 
 
     def test_lookup(self):
@@ -86,6 +123,58 @@ class StyleTest(AbstractTkTest, unittest.TestCase):
         self.style.theme_use(curr_theme)
 
 
+    def test_configure_custom_copy(self):
+        style = self.style
+
+        curr_theme = self.style.theme_use()
+        self.addCleanup(self.style.theme_use, curr_theme)
+        for theme in self.style.theme_names():
+            self.style.theme_use(theme)
+            for name in CLASS_NAMES:
+                default = style.configure(name)
+                if not default:
+                    continue
+                with self.subTest(theme=theme, name=name):
+                    if support.verbose >= 2:
+                        print('configure', theme, name, default)
+                    if (theme in ('vista', 'xpnative')
+                            and sys.getwindowsversion()[:2] == (6, 1)):
+                        # Fails on the Windows 7 buildbot
+                        continue
+                    newname = f'C.{name}'
+                    self.assertEqual(style.configure(newname), None)
+                    style.configure(newname, **default)
+                    self.assertEqual(style.configure(newname), default)
+                    for key, value in default.items():
+                        self.assertEqual(style.configure(newname, key), value)
+
+
+    def test_map_custom_copy(self):
+        style = self.style
+
+        curr_theme = self.style.theme_use()
+        self.addCleanup(self.style.theme_use, curr_theme)
+        for theme in self.style.theme_names():
+            self.style.theme_use(theme)
+            for name in CLASS_NAMES:
+                default = style.map(name)
+                if not default:
+                    continue
+                with self.subTest(theme=theme, name=name):
+                    if support.verbose >= 2:
+                        print('map', theme, name, default)
+                    if (theme in ('vista', 'xpnative')
+                            and sys.getwindowsversion()[:2] == (6, 1)):
+                        # Fails on the Windows 7 buildbot
+                        continue
+                    newname = f'C.{name}'
+                    self.assertEqual(style.map(newname), {})
+                    style.map(newname, **default)
+                    self.assertEqual(style.map(newname), default)
+                    for key, value in default.items():
+                        self.assertEqual(style.map(newname, key), value)
+
+
 tests_gui = (StyleTest, )
 
 if __name__ == "__main__":
index 2598bc6..157ef0e 100644 (file)
@@ -60,11 +60,10 @@ class WidgetTest(AbstractTkTest, unittest.TestCase):
         super().setUp()
         self.widget = ttk.Button(self.root, width=0, text="Text")
         self.widget.pack()
-        self.widget.wait_visibility()
 
 
     def test_identify(self):
-        self.widget.update_idletasks()
+        self.widget.update()
         self.assertEqual(self.widget.identify(
             int(self.widget.winfo_width() / 2),
             int(self.widget.winfo_height() / 2)
@@ -326,8 +325,7 @@ class EntryTest(AbstractWidgetTest, unittest.TestCase):
 
     def test_identify(self):
         self.entry.pack()
-        self.entry.wait_visibility()
-        self.entry.update_idletasks()
+        self.entry.update()
 
         # bpo-27313: macOS Cocoa widget differs from X, allow either
         if sys.platform == 'darwin':
@@ -437,11 +435,12 @@ class ComboboxTest(EntryTest, unittest.TestCase):
 
     def _show_drop_down_listbox(self):
         width = self.combo.winfo_width()
-        self.combo.event_generate('<ButtonPress-1>', x=width - 5, y=5)
-        self.combo.event_generate('<ButtonRelease-1>', x=width - 5, y=5)
+        x, y = width - 5, 5
+        self.assertRegex(self.combo.identify(x, y), r'.*downarrow\Z')
+        self.combo.event_generate('<ButtonPress-1>', x=x, y=y)
+        self.combo.event_generate('<ButtonRelease-1>', x=x, y=y)
         self.combo.update_idletasks()
 
-
     def test_virtual_event(self):
         success = []
 
@@ -449,7 +448,7 @@ class ComboboxTest(EntryTest, unittest.TestCase):
         self.combo.bind('<<ComboboxSelected>>',
             lambda evt: success.append(True))
         self.combo.pack()
-        self.combo.wait_visibility()
+        self.combo.update()
 
         height = self.combo.winfo_height()
         self._show_drop_down_listbox()
@@ -465,7 +464,7 @@ class ComboboxTest(EntryTest, unittest.TestCase):
 
         self.combo['postcommand'] = lambda: success.append(True)
         self.combo.pack()
-        self.combo.wait_visibility()
+        self.combo.update()
 
         self._show_drop_down_listbox()
         self.assertTrue(success)
@@ -665,7 +664,6 @@ class PanedWindowTest(AbstractWidgetTest, unittest.TestCase):
         self.assertRaises(tkinter.TclError, self.paned.sashpos, 1)
 
         self.paned.pack(expand=True, fill='both')
-        self.paned.wait_visibility()
 
         curr_pos = self.paned.sashpos(0)
         self.paned.sashpos(0, 1000)
@@ -933,7 +931,7 @@ class NotebookTest(AbstractWidgetTest, unittest.TestCase):
         self.nb.add(self.child1, text='a')
 
         self.nb.pack()
-        self.nb.wait_visibility()
+        self.nb.update()
         if sys.platform == 'darwin':
             tb_idx = "@20,5"
         else:
@@ -1041,7 +1039,7 @@ class NotebookTest(AbstractWidgetTest, unittest.TestCase):
 
     def test_select(self):
         self.nb.pack()
-        self.nb.wait_visibility()
+        self.nb.update()
 
         success = []
         tab_changed = []
@@ -1084,10 +1082,11 @@ class NotebookTest(AbstractWidgetTest, unittest.TestCase):
 
     def test_traversal(self):
         self.nb.pack()
-        self.nb.wait_visibility()
+        self.nb.update()
 
         self.nb.select(0)
 
+        self.assertEqual(self.nb.identify(5, 5), 'focus')
         simulate_mouse_click(self.nb, 5, 5)
         self.nb.focus_force()
         self.nb.event_generate('<Control-Tab>')
@@ -1102,6 +1101,7 @@ class NotebookTest(AbstractWidgetTest, unittest.TestCase):
         self.nb.tab(self.child1, text='a', underline=0)
         self.nb.enable_traversal()
         self.nb.focus_force()
+        self.assertEqual(self.nb.identify(5, 5), 'focus')
         simulate_mouse_click(self.nb, 5, 5)
         if sys.platform == 'darwin':
             self.nb.event_generate('<Option-a>')
@@ -1132,6 +1132,7 @@ class SpinboxTest(EntryTest, unittest.TestCase):
         height = self.spin.winfo_height()
         x = width - 5
         y = height//2 - 5
+        self.assertRegex(self.spin.identify(x, y), r'.*uparrow\Z')
         self.spin.event_generate('<ButtonPress-1>', x=x, y=y)
         self.spin.event_generate('<ButtonRelease-1>', x=x, y=y)
         self.spin.update_idletasks()
@@ -1141,6 +1142,7 @@ class SpinboxTest(EntryTest, unittest.TestCase):
         height = self.spin.winfo_height()
         x = width - 5
         y = height//2 + 4
+        self.assertRegex(self.spin.identify(x, y), r'.*downarrow\Z')
         self.spin.event_generate('<ButtonPress-1>', x=x, y=y)
         self.spin.event_generate('<ButtonRelease-1>', x=x, y=y)
         self.spin.update_idletasks()
@@ -1342,7 +1344,6 @@ class TreeviewTest(AbstractWidgetTest, unittest.TestCase):
     def test_bbox(self):
         self.tv.pack()
         self.assertEqual(self.tv.bbox(''), '')
-        self.tv.wait_visibility()
         self.tv.update()
 
         item_id = self.tv.insert('', 'end')
@@ -1530,13 +1531,15 @@ class TreeviewTest(AbstractWidgetTest, unittest.TestCase):
 
     def test_heading_callback(self):
         def simulate_heading_click(x, y):
+            if tcl_version >= (8, 6):
+                self.assertEqual(self.tv.identify_column(x), '#0')
+                self.assertEqual(self.tv.identify_region(x, y), 'heading')
             simulate_mouse_click(self.tv, x, y)
             self.tv.update()
 
         success = [] # no success for now
 
         self.tv.pack()
-        self.tv.wait_visibility()
         self.tv.heading('#0', command=lambda: success.append(True))
         self.tv.column('#0', width=100)
         self.tv.update()
@@ -1784,7 +1787,6 @@ class TreeviewTest(AbstractWidgetTest, unittest.TestCase):
             lambda evt: events.append(2))
 
         self.tv.pack()
-        self.tv.wait_visibility()
         self.tv.update()
 
         pos_y = set()
index c7c71cd..968fd54 100644 (file)
@@ -81,8 +81,6 @@ def _mapdict_values(items):
     #   ['active selected', 'grey', 'focus', [1, 2, 3, 4]]
     opt_val = []
     for *state, val in items:
-        # hacks for backward compatibility
-        state[0] # raise IndexError if empty
         if len(state) == 1:
             # if it is empty (something that evaluates to False), then
             # format it to Tcl code to denote the "normal" state
@@ -243,19 +241,22 @@ def _script_from_settings(settings):
 def _list_from_statespec(stuple):
     """Construct a list from the given statespec tuple according to the
     accepted statespec accepted by _format_mapdict."""
-    nval = []
-    for val in stuple:
-        typename = getattr(val, 'typename', None)
-        if typename is None:
-            nval.append(val)
-        else: # this is a Tcl object
+    if isinstance(stuple, str):
+        return stuple
+    result = []
+    it = iter(stuple)
+    for state, val in zip(it, it):
+        if hasattr(state, 'typename'):  # this is a Tcl object
+            state = str(state).split()
+        elif isinstance(state, str):
+            state = state.split()
+        elif not isinstance(state, (tuple, list)):
+            state = (state,)
+        if hasattr(val, 'typename'):
             val = str(val)
-            if typename == 'StateSpec':
-                val = val.split()
-            nval.append(val)
+        result.append((*state, val))
 
-    it = iter(nval)
-    return [_flatten(spec) for spec in zip(it, it)]
+    return result
 
 def _list_from_layouttuple(tk, ltuple):
     """Construct a list from the tuple returned by ttk::layout, this is
@@ -395,13 +396,12 @@ class Style(object):
         or something else of your preference. A statespec is compound of
         one or more states and then a value."""
         if query_opt is not None:
-            return _list_from_statespec(self.tk.splitlist(
-                self.tk.call(self._name, "map", style, '-%s' % query_opt)))
+            result = self.tk.call(self._name, "map", style, '-%s' % query_opt)
+            return _list_from_statespec(self.tk.splitlist(result))
 
-        return _splitdict(
-            self.tk,
-            self.tk.call(self._name, "map", style, *_format_mapdict(kw)),
-            conv=_tclobj_to_py)
+        result = self.tk.call(self._name, "map", style, *_format_mapdict(kw))
+        return {k: _list_from_statespec(self.tk.splitlist(v))
+                for k, v in _splitdict(self.tk, result).items()}
 
 
     def lookup(self, style, option, state=None, default=None):
index a19e387..fb34de9 100644 (file)
@@ -500,7 +500,6 @@ class TracebackException:
                 _seen=_seen)
         else:
             context = None
-        self.exc_traceback = exc_traceback
         self.__cause__ = cause
         self.__context__ = context
         self.__suppress_context__ = \
@@ -617,7 +616,7 @@ class TracebackException:
                 not self.__suppress_context__):
                 yield from self.__context__.format(chain=chain)
                 yield _context_message
-        if self.exc_traceback is not None:
+        if self.stack:
             yield 'Traceback (most recent call last):\n'
-        yield from self.stack.format()
+            yield from self.stack.format()
         yield from self.format_exception_only()
index 39c956d..f5316ab 100644 (file)
@@ -160,6 +160,8 @@ def _type_repr(obj):
     typically enough to uniquely identify a type.  For everything
     else, we fall back on repr(obj).
     """
+    if isinstance(obj, types.GenericAlias):
+        return repr(obj)
     if isinstance(obj, type):
         if obj.__module__ == 'builtins':
             return obj.__qualname__
@@ -198,6 +200,20 @@ def _check_generic(cls, parameters, elen):
                         f" actual {alen}, expected {elen}")
 
 
+def _deduplicate(params):
+    # Weed out strict duplicates, preserving the first of each occurrence.
+    all_params = set(params)
+    if len(all_params) < len(params):
+        new_params = []
+        for t in params:
+            if t in all_params:
+                new_params.append(t)
+                all_params.remove(t)
+        params = new_params
+        assert not all_params, all_params
+    return params
+
+
 def _remove_dups_flatten(parameters):
     """An internal helper for Union creation and substitution: flatten Unions
     among parameters, then remove duplicates.
@@ -211,38 +227,45 @@ def _remove_dups_flatten(parameters):
             params.extend(p[1:])
         else:
             params.append(p)
-    # Weed out strict duplicates, preserving the first of each occurrence.
-    all_params = set(params)
-    if len(all_params) < len(params):
-        new_params = []
-        for t in params:
-            if t in all_params:
-                new_params.append(t)
-                all_params.remove(t)
-        params = new_params
-        assert not all_params, all_params
+
+    return tuple(_deduplicate(params))
+
+
+def _flatten_literal_params(parameters):
+    """An internal helper for Literal creation: flatten Literals among parameters"""
+    params = []
+    for p in parameters:
+        if isinstance(p, _LiteralGenericAlias):
+            params.extend(p.__args__)
+        else:
+            params.append(p)
     return tuple(params)
 
 
 _cleanups = []
 
 
-def _tp_cache(func):
+def _tp_cache(func=None, /, *, typed=False):
     """Internal wrapper caching __getitem__ of generic types with a fallback to
     original function for non-hashable arguments.
     """
-    cached = functools.lru_cache()(func)
-    _cleanups.append(cached.cache_clear)
+    def decorator(func):
+        cached = functools.lru_cache(typed=typed)(func)
+        _cleanups.append(cached.cache_clear)
 
-    @functools.wraps(func)
-    def inner(*args, **kwds):
-        try:
-            return cached(*args, **kwds)
-        except TypeError:
-            pass  # All real errors (not unhashable args) are raised below.
-        return func(*args, **kwds)
-    return inner
+        @functools.wraps(func)
+        def inner(*args, **kwds):
+            try:
+                return cached(*args, **kwds)
+            except TypeError:
+                pass  # All real errors (not unhashable args) are raised below.
+            return func(*args, **kwds)
+        return inner
+
+    if func is not None:
+        return decorator(func)
 
+    return decorator
 
 def _eval_type(t, globalns, localns, recursive_guard=frozenset()):
     """Evaluate all forward references in the given type t.
@@ -315,6 +338,13 @@ class _SpecialForm(_Final, _root=True):
     def __getitem__(self, parameters):
         return self._getitem(self, parameters)
 
+
+class _LiteralSpecialForm(_SpecialForm, _root=True):
+    @_tp_cache(typed=True)
+    def __getitem__(self, parameters):
+        return self._getitem(self, parameters)
+
+
 @_SpecialForm
 def Any(self, parameters):
     """Special type indicating an unconstrained type.
@@ -432,7 +462,7 @@ def Optional(self, parameters):
     arg = _type_check(parameters, f"{self} requires a single type.")
     return Union[arg, type(None)]
 
-@_SpecialForm
+@_LiteralSpecialForm
 def Literal(self, parameters):
     """Special typing form to define literal types (a.k.a. value types).
 
@@ -456,7 +486,17 @@ def Literal(self, parameters):
     """
     # There is no '_type_check' call because arguments to Literal[...] are
     # values, not types.
-    return _GenericAlias(self, parameters)
+    if not isinstance(parameters, tuple):
+        parameters = (parameters,)
+
+    parameters = _flatten_literal_params(parameters)
+
+    try:
+        parameters = tuple(p for p, _ in _deduplicate(list(_value_and_type_iter(parameters))))
+    except TypeError:  # unhashable parameters
+        pass
+
+    return _LiteralGenericAlias(self, parameters)
 
 
 class ForwardRef(_Final, _root=True):
@@ -879,6 +919,22 @@ class _UnionGenericAlias(_GenericAlias, _root=True):
         return super().__repr__()
 
 
+def _value_and_type_iter(parameters):
+    return ((p, type(p)) for p in parameters)
+
+
+class _LiteralGenericAlias(_GenericAlias, _root=True):
+
+    def __eq__(self, other):
+        if not isinstance(other, _LiteralGenericAlias):
+            return NotImplemented
+
+        return set(_value_and_type_iter(self.__args__)) == set(_value_and_type_iter(other.__args__))
+
+    def __hash__(self):
+        return hash(frozenset(_value_and_type_iter(self.__args__)))
+
+
 class Generic:
     """Abstract base class for generic types.
 
index 2a3d715..a8c870b 100644 (file)
@@ -2596,6 +2596,11 @@ def _proxy_bypass_macosx_sysconf(host, proxy_settings):
                 mask = 8 * (m.group(1).count('.') + 1)
             else:
                 mask = int(mask[1:])
+
+            if mask < 0 or mask > 32:
+                # System libraries ignore invalid prefix lengths
+                continue
+
             mask = 32 - mask
 
             if (hostIP >> mask) == (base >> mask):
index cea9130..6023c1e 100755 (executable)
@@ -550,7 +550,7 @@ def register_standard_browsers():
                 cmd = "xdg-settings get default-web-browser".split()
                 raw_result = subprocess.check_output(cmd, stderr=subprocess.DEVNULL)
                 result = raw_result.decode().strip()
-            except (FileNotFoundError, subprocess.CalledProcessError, PermissionError) :
+            except (FileNotFoundError, subprocess.CalledProcessError, PermissionError, NotADirectoryError) :
                 pass
             else:
                 global _os_preferred_browser
index da2bcad..7a26900 100644 (file)
@@ -1876,6 +1876,11 @@ class C14NWriterTarget:
                 self._declared_ns_stack[-1].append((uri, prefix))
                 return f'{prefix}:{tag}' if prefix else tag, tag, uri
 
+        if not uri:
+            # As soon as a default namespace is defined,
+            # anything that has no namespace (and thus, no prefix) goes there.
+            return tag, tag, uri
+
         raise ValueError(f'Namespace "{uri}" is not declared in scope')
 
     def data(self, data):
index 915698f..816f858 100644 (file)
@@ -1918,6 +1918,8 @@ class ZipFile:
                              centDirSize, centDirOffset, len(self._comment))
         self.fp.write(endrec)
         self.fp.write(self._comment)
+        if self.mode == "a":
+            self.fp.truncate()
         self.fp.flush()
 
     def _fpclose(self, fp):
index a58b922..16816d1 100755 (executable)
@@ -116,7 +116,8 @@ WORKDIR = "/tmp/_py"
 DEPSRC = os.path.join(WORKDIR, 'third-party')
 DEPSRC = os.path.expanduser('~/Universal/other-sources')
 
-universal_opts_map = { '32-bit': ('i386', 'ppc',),
+universal_opts_map = { 'universal2': ('arm64', 'x86_64'),
+                       '32-bit': ('i386', 'ppc',),
                        '64-bit': ('x86_64', 'ppc64',),
                        'intel':  ('i386', 'x86_64'),
                        'intel-32':  ('i386',),
@@ -124,6 +125,7 @@ universal_opts_map = { '32-bit': ('i386', 'ppc',),
                        '3-way':  ('ppc', 'i386', 'x86_64'),
                        'all':    ('i386', 'ppc', 'x86_64', 'ppc64',) }
 default_target_map = {
+        'universal2': '10.9',
         '64-bit': '10.5',
         '3-way': '10.5',
         'intel': '10.5',
@@ -151,6 +153,9 @@ DEPTARGET = '10.5'
 def getDeptargetTuple():
     return tuple([int(n) for n in DEPTARGET.split('.')[0:2]])
 
+def getBuildTuple():
+    return tuple([int(n) for n in platform.mac_ver()[0].split('.')[0:2]])
+
 def getTargetCompilers():
     target_cc_map = {
         '10.4': ('gcc-4.0', 'g++-4.0'),
@@ -190,6 +195,34 @@ EXPECTED_SHARED_LIBS = {}
 def internalTk():
     return getDeptargetTuple() >= (10, 6)
 
+# Do we use 8.6.8 when building our own copy
+# of Tcl/Tk or a modern version.
+#   We use the old version when buildin on
+#   old versions of macOS due to build issues.
+def useOldTk():
+    return getBuildTuple() < (10, 15)
+
+
+def tweak_tcl_build(basedir, archList):
+    with open("Makefile", "r") as fp:
+        contents = fp.readlines()
+
+    # For reasons I don't understand the tcl configure script
+    # decides that some stdlib symbols aren't present, before
+    # deciding that strtod is broken.
+    new_contents = []
+    for line in contents:
+        if line.startswith("COMPAT_OBJS"):
+            # note: the space before strtod.o is intentional,
+            # the detection of a broken strtod results in
+            # "fixstrod.o" on this line.
+            for nm in ("strstr.o", "strtoul.o", " strtod.o"):
+                line = line.replace(nm, "")
+        new_contents.append(line)
+
+    with open("Makefile", "w") as fp:
+        fp.writelines(new_contents)
+
 # List of names of third party software built with this installer.
 # The names will be inserted into the rtf version of the License.
 THIRD_PARTY_LIBS = []
@@ -215,15 +248,33 @@ def library_recipes():
               buildrecipe=build_universal_openssl,
               configure=None,
               install=None,
+              patches=[
+                  "openssl-mac-arm64.patch",
+                   ],
           ),
     ])
 
     if internalTk():
+        if useOldTk():
+            tcl_tk_ver='8.6.8'
+            tcl_checksum='81656d3367af032e0ae6157eff134f89'
+
+            tk_checksum='5e0faecba458ee1386078fb228d008ba'
+            tk_patches = ['tk868_on_10_8_10_9.patch']
+
+        else:
+            tcl_tk_ver='8.6.10'
+            tcl_checksum='97c55573f8520bcab74e21bfd8d0aadc'
+
+            tk_checksum='602a47ad9ecac7bf655ada729d140a94'
+            tk_patches = [ ]
+
+
         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 %s"%(tcl_tk_ver,),
+              url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tcl%s-src.tar.gz"%(tcl_tk_ver,),
+              checksum=tcl_checksum,
               buildDir="unix",
               configure_pre=[
                     '--enable-shared',
@@ -231,18 +282,17 @@ def library_recipes():
                     '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
               ],
               useLDFlags=False,
+              buildrecipe=tweak_tcl_build,
               install='make TCL_LIBRARY=%(TCL_LIBRARY)s && make install TCL_LIBRARY=%(TCL_LIBRARY)s DESTDIR=%(DESTDIR)s'%{
                   "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
                   "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.6'%(getVersion())),
                   },
               ),
           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 %s"%(tcl_tk_ver,),
+              url="ftp://ftp.tcl.tk/pub/tcl//tcl8_6/tk%s-src.tar.gz"%(tcl_tk_ver,),
+              checksum=tk_checksum,
+              patches=tk_patches,
               buildDir="unix",
               configure_pre=[
                     '--enable-aqua',
@@ -307,9 +357,9 @@ def library_recipes():
                   ),
           ),
           dict(
-              name="SQLite 3.32.3",
-              url="https://sqlite.org/2020/sqlite-autoconf-3320300.tar.gz",
-              checksum='2e3911a3c15e85c2f2d040154bbe5ce3',
+              name="SQLite 3.33.0",
+              url="https://sqlite.org/2020/sqlite-autoconf-3330000.tar.gz",
+              checksum='842a8a100d7b01b09e543deb2b7951dd',
               extra_cflags=('-Os '
                             '-DSQLITE_ENABLE_FTS5 '
                             '-DSQLITE_ENABLE_FTS4 '
@@ -537,8 +587,8 @@ def checkEnvironment():
     Check that we're running on a supported system.
     """
 
-    if sys.version_info[0:2] < (2, 5):
-        fatal("This script must be run with Python 2.5 (or later)")
+    if sys.version_info[0:2] < (2, 7):
+        fatal("This script must be run with Python 2.7 (or later)")
 
     if platform.system() != 'Darwin':
         fatal("This script should be run on a macOS 10.5 (or later) system")
@@ -606,9 +656,6 @@ def checkEnvironment():
         base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
     os.environ['PATH'] = base_path
     print("Setting default PATH: %s"%(os.environ['PATH']))
-    # Ensure we have access to sphinx-build.
-    # You may have to create a link in /usr/bin for it.
-    runCommand('sphinx-build --version')
 
 def parseOptions(args=None):
     """
@@ -801,6 +848,7 @@ def build_universal_openssl(basedir, archList):
         arch_opts = {
             "i386": ["darwin-i386-cc"],
             "x86_64": ["darwin64-x86_64-cc", "enable-ec_nistp_64_gcc_128"],
+            "arm64": ["darwin64-arm64-cc"],
             "ppc": ["darwin-ppc-cc"],
             "ppc64": ["darwin64-ppc-cc"],
         }
@@ -1567,8 +1615,17 @@ def buildDMG():
     if os.path.exists(outdir):
         shutil.rmtree(outdir)
 
+    # We used to use the deployment target as the last characters of the 
+    # installer file name. With the introduction of weaklinked installer
+    # variants, we may have two variants with the same file name, i.e.
+    # both ending in '10.9'.  To avoid this, we now use the major/minor
+    # version numbers of the macOS version we are building on, i.e.
+    # '10.9' as before for 10.9+ variant, '11.0' for universal2 11.0-.
+    # it's not ideal but should cause the least disruption to packaging
+    # workflows.
+    build_system_version = '.'.join(platform.mac_ver()[0].split('.')[0:2])
     imagepath = os.path.join(outdir,
-                    'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
+                    'python-%s-macosx%s'%(getFullVersion(),build_system_version))
     if INCLUDE_TIMESTAMP:
         imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
     imagepath = imagepath + '.dmg'
diff --git a/Mac/BuildScript/openssl-mac-arm64.patch b/Mac/BuildScript/openssl-mac-arm64.patch
new file mode 100644 (file)
index 0000000..11267fb
--- /dev/null
@@ -0,0 +1,41 @@
+diff -ur openssl-1.1.1g-orig/Configurations/10-main.conf openssl-1.1.1g/Configurations/10-main.conf
+--- openssl-1.1.1g-orig/Configurations/10-main.conf    2020-04-21 14:22:39.000000000 +0200
++++ openssl-1.1.1g/Configurations/10-main.conf 2020-07-26 12:21:32.000000000 +0200
+@@ -1557,6 +1557,14 @@
+         bn_ops           => "SIXTY_FOUR_BIT_LONG",
+         perlasm_scheme   => "macosx",
+     },
++    "darwin64-arm64-cc" => {
++        inherit_from     => [ "darwin-common", asm("aarch64_asm") ],
++        CFLAGS           => add("-Wall"),
++        cflags           => add("-arch arm64"),
++        lib_cppflags     => add("-DL_ENDIAN"),
++        bn_ops           => "SIXTY_FOUR_BIT_LONG",
++        perlasm_scheme   => "ios64",
++    },
+ ##### GNU Hurd
+     "hurd-x86" => {
+diff -ur openssl-1.1.1g-orig/config openssl-1.1.1g/config
+--- openssl-1.1.1g-orig/config 2020-04-21 14:22:39.000000000 +0200
++++ openssl-1.1.1g/config      2020-07-26 12:21:59.000000000 +0200
+@@ -255,6 +255,9 @@
+               ;;
+           x86_64)
+               echo "x86_64-apple-darwin${VERSION}"
++                ;;
++          arm64)
++              echo "arm64-apple-darwin${VERSION}"
+               ;;
+           *)
+               echo "i686-apple-darwin${VERSION}"
+@@ -497,6 +500,9 @@
+       else
+           OUT="darwin64-x86_64-cc"
+       fi ;;
++  x86_64-apple-darwin*)
++      OUT="darwin64-arm64-cc"
++        ;;
+   armv6+7-*-iphoneos)
+       __CNF_CFLAGS="$__CNF_CFLAGS -arch armv6 -arch armv7"
+       __CNF_CXXFLAGS="$__CNF_CXXFLAGS -arch armv6 -arch armv7"
index 9bc9698..2c0c3d5 100644 (file)
@@ -3,7 +3,7 @@
 \f3\fmodern\fcharset0 CourierNewPSMT;}
 {\colortbl;\red255\green255\blue255;}
 {\*\expandedcolortbl;;}
-\margl1440\margr1440\vieww13380\viewh14600\viewkind0
+\margl1440\margr1440\vieww13380\viewh14580\viewkind0
 \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0
 
 \f0\fs24 \cf0 This package will install Python $FULL_VERSION for macOS $MACOSX_DEPLOYMENT_TARGET for the following architecture(s): $ARCHITECTURES.\
@@ -50,7 +50,21 @@ Due to new security checks on macOS 10.15 Catalina, when launching IDLE macOS ma
 \f0\b0  button to proceed.\
 \
 
-\f1\b \ul Other changes\
+\f1\b \ul macOS 11.0 (Big Sur) and Apple Silicon Mac support [new in 3.9.1]\
+
+\f0\b0 \ulnone \
+As of 2020-11, macOS 11.0 (Big Sur) is the latest release of macOS and one of its major features is the support of new Apple Silicon Macs that are based on the ARM64 CPU architecture specification rather than the Intel 64 (x86_64) architecture used previously. There are other changes in Big Sur that affect Python operation regardless of CPU architecture. As of 3.9.1, Python binaries from python.org fully support Big Sur. \
+\
+python.org binaries for macOS have been provided via a downloadable installer that supports the Intel 64 architecture on macOS 10.9 and newer.  This installer variant remains the default download for 3.9.1;  it will install and run on all Macs that run macOS 10.9 or later, including 11.0 (Big Sur). This variant should run transparently on new Apple Silicon Macs using Apple's Rosetta 2 emulation. \
+\
+Beginning with 3.9.1, we also provide a new "universal2" installer variant that provides universal binaries for both ARM64 and Intel 64 architectures and is also supported on all Macs that support macOS 10.9 or later.  Some of the advantages of the new installer variant: native ARM64 code on Apple Silicon Macs should run significantly faster than Rosetta2-emulated code; some operating system functions and options introduced in macOS releases since 10.9 are now exposed when available (primarily in the os module); binary wheels built for use with the current 10.9 variant *should* also work with the new variant; the new installer variant includes Tcl/Tk 8.6.10 rather than 8.6.8.\
+\
+Because of the scope of changes needed to fully support 11.0 and Apple Silicon Macs, the new "universal2" variant should be considered 
+\f2\i experimental
+\f0\i0  in the 3.9.1 release. You may need to upgrade third-party components, like pip, to later versions once they are released. You may experience differences in behavior in IDLE and other Tk-based applications due to using the newer version of Tk.  As always, if you encounter problems when using this installer variant, please check {\field{\*\fldinst{HYPERLINK "https://bugs.python.org"}}{\fldrslt https://bugs.python.org}} for existing reports and for opening new issues.\
+
+\f1\b \ul \
+Other changes\
 
 \f0\b0 \ulnone \
 For other changes in this release, see the 
index ec7d873..f3638aa 100644 (file)
@@ -120,6 +120,8 @@ support ppc (Xcode 4 on 10.6 and later systems).  The flavor can be specified
 using the configure option ``--with-universal-archs=VALUE``. The following
 values are available:
 
+  * ``universal2``: ``arm64``, ``x86_64``
+
   * ``intel``:   ``i386``, ``x86_64``
 
   * ``intel-32``: ``i386``
@@ -155,6 +157,8 @@ following combinations of SDKs and universal-archs flavors are available:
 
   * 10.15 and later SDKs support ``intel-64`` only
 
+  * 11.0 and later SDKs support ``universal2``
+
 The makefile for a framework build will also install ``python3.x-32``
 binaries when the universal architecture includes at least one 32-bit
 architecture (that is, for all flavors but ``64-bit`` and ``intel-64``).
@@ -352,6 +356,39 @@ A framework install also installs some applications in ``/Applications/Python X.
 And lastly a framework installation installs files in ``/usr/local/bin``, all of
 them symbolic links to files in ``/Library/Frameworks/Python.framework/Versions/X.Y/bin``.
 
+Weak linking support
+====================
+
+The CPython sources support building with the latest SDK while targetting deployment
+to macOS 10.9. This is done through weak linking of symbols introduced in macOS
+10.10 or later and checking for their availability at runtime.
+
+This requires the use of Apple's compiler toolchain on macOS 10.13 or later.
+
+The basic implementation pattern is:
+
+* ``HAVE_<FUNCTION>`` is a macro defined (or not) by the configure script
+
+* ``HAVE_<FUNCTION>_RUNTIME`` is a macro defined in the relevant source
+  files. This expands to a call to ``__builtin_available`` when using
+  a new enough Apple compiler, and to a true value otherwise.
+
+* Use ``HAVE_<FUNCTION>_RUNTIME`` before calling ``<function>``. This macro
+  *must* be used a the sole expression in an if statement::
+
+   if (HAVE_<FUNCTION>_RUNTIME) {
+     /* <function> is available */
+   }
+
+  Or:
+
+   if (HAVE_<FUNCTION>_RUNTIME) {} else {
+     /* <function> is not available */
+   }
+
+  Using other patterns (such as ``!HAVE_<FUNCTION>_RUNTIME``) is not supported
+  by Apple's compilers.
+
 
 Resources
 =========
index c8bd3ba..78813e8 100644 (file)
@@ -95,9 +95,6 @@ setup_spawnattr(posix_spawnattr_t* spawnattr)
     size_t count;
     cpu_type_t cpu_types[1];
     short flags = 0;
-#ifdef __LP64__
-    int   ch;
-#endif
 
     if ((errno = posix_spawnattr_init(spawnattr)) != 0) {
         err(2, "posix_spawnattr_int");
@@ -119,10 +116,16 @@ setup_spawnattr(posix_spawnattr_t* spawnattr)
 
 #elif defined(__ppc__)
     cpu_types[0] = CPU_TYPE_POWERPC;
+
 #elif defined(__i386__)
     cpu_types[0] = CPU_TYPE_X86;
+
+#elif defined(__arm64__)
+    cpu_types[0] = CPU_TYPE_ARM64;
+
 #else
 #       error "Unknown CPU"
+
 #endif
 
     if (posix_spawnattr_setbinpref_np(spawnattr, count,
@@ -220,7 +223,8 @@ main(int argc, char **argv) {
     /* We're weak-linking to posix-spawnv to ensure that
      * an executable build on 10.5 can work on 10.4.
      */
-    if (posix_spawn != NULL) {
+
+    if (&posix_spawn != NULL) {
         posix_spawnattr_t spawnattr = NULL;
 
         setup_spawnattr(&spawnattr);
index 77f91e7..f128444 100644 (file)
@@ -1114,6 +1114,7 @@ PYTHON_HEADERS= \
                $(srcdir)/Include/cpython/dictobject.h \
                $(srcdir)/Include/cpython/fileobject.h \
                $(srcdir)/Include/cpython/fileutils.h \
+               $(srcdir)/Include/cpython/frameobject.h \
                $(srcdir)/Include/cpython/import.h \
                $(srcdir)/Include/cpython/initconfig.h \
                $(srcdir)/Include/cpython/interpreteridobject.h \
index a16f15a..12a5ac1 100644 (file)
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -70,6 +70,7 @@ Alexandru Ardelean
 Emmanuel Arias
 Alicia Arlen
 Jeffrey Armstrong
+Justin Turner Arthur
 Jason Asbahr
 David Ascher
 Ammar Askar
@@ -242,6 +243,7 @@ Colm Buckley
 Erik de Bueger
 Jan-Hein Bührman
 Lars Buitinck
+Artem Bulgakov
 Dick Bulterman
 Bill Bumgarner
 Jimmy Burgett
@@ -377,6 +379,7 @@ Brian Curtin
 Jason Curtis
 Hakan Celik
 Paul Dagnelie
+Florian Dahlitz 
 Lisandro Dalcin
 Darren Dale
 Andrew Dalke
@@ -781,6 +784,7 @@ Meador Inge
 Peter Ingebretson
 Tony Ingraldi
 John Interrante
+Dean Inwood
 Bob Ippolito
 Roger Irwin
 Atsuo Ishimoto
@@ -851,6 +855,7 @@ Jan Kanis
 Rafe Kaplan
 Jacob Kaplan-Moss
 Allison Kaptur
+Yurii Karabas
 Janne Karila
 Per Øyvind Karlsen
 Anton Kasyanov
@@ -1939,5 +1944,6 @@ Gennadiy Zlobin
 Doug Zongker
 Peter Åstrand
 Vlad Emelianov
+Andrey Doroschenko
 
 (Entries should be added in rough alphabetical order by last names)
index 81da2be..03c84f3 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -2,6 +2,438 @@
 Python News
 +++++++++++
 
+What's New in Python 3.9.1 final?
+=================================
+
+*Release date: 2020-12-07*
+
+Core and Builtins
+-----------------
+
+- bpo-42576: ``types.GenericAlias`` will now raise a ``TypeError`` when
+  attempting to initialize with a keyword argument.  Previously, this would
+  cause the interpreter to crash if the interpreter was compiled with debug
+  symbols. This does not affect interpreters compiled for release.  Patch by
+  Ken Jin.
+
+Library
+-------
+
+- bpo-5054: CGIHTTPRequestHandler.run_cgi() HTTP_ACCEPT improperly parsed.
+  Replace the special purpose getallmatchingheaders with generic get_all
+  method and add relevant tests.
+
+  Original Patch by Martin Panter. Modified by Senthil Kumaran.
+
+- bpo-17735: :func:`inspect.findsource` now raises :exc:`OSError` instead of
+  :exc:`IndexError` when :attr:`co_lineno` of a code object is greater than
+  the file length. This can happen, for example, when a file is edited after
+  it was imported.  PR by Irit Katriel.
+
+- bpo-42116: Fix handling of trailing comments by :func:`inspect.getsource`.
+
+- bpo-42487: ChainMap.__iter__ no longer calls __getitem__ on underlying
+  maps
+
+- bpo-42482: :class:`~traceback.TracebackException` no longer holds a
+  reference to the exception's traceback object. Consequently, instances of
+  TracebackException for equivalent but non-equal exceptions now compare as
+  equal.
+
+- bpo-42406: We fixed an issue in `pickle.whichmodule` in which importing
+  `multiprocessing` could change the how pickle identifies which module an
+  object belongs to, potentially breaking the unpickling of those objects.
+
+- bpo-34215: Clarify the error message for
+  :exc:`asyncio.IncompleteReadError` when ``expected`` is ``None``.
+
+- bpo-12800: Extracting a symlink from a tarball should succeed and
+  overwrite the symlink if it already exists. The fix is to remove the
+  existing file or symlink before extraction. Based on patch by Chris AtLee,
+  Jeffrey Kintscher, and Senthil Kumaran.
+
+Tests
+-----
+
+- bpo-41473: Reenable test_gdb on gdb 9.2 and newer:
+  https://bugzilla.redhat.com/show_bug.cgi?id=1866884 bug is fixed in gdb
+  10.1.
+
+- bpo-42553: Fix ``test_asyncio.test_call_later()`` race condition: don't
+  measure asyncio performance in the ``call_later()`` unit test. The test
+  failed randomly on the CI.
+
+macOS
+-----
+
+- bpo-41116: If no explicit macOS SDK was specified, setup.py should check
+  for Tcl and TK frameworks in /Library/Frameworks; the previous commit
+  inadvertently broke that test.
+
+- bpo-42504: Fix build on macOS Big Sur when MACOSX_DEPLOYMENT_TARGET=11
+
+IDLE
+----
+
+- bpo-42508: Keep IDLE running on macOS.  Remove obsolete workaround that
+  prevented running files with shortcuts when using new universal2
+  installers built on macOS 11.
+
+
+What's New in Python 3.9.1 release candidate 1?
+===============================================
+
+*Release date: 2020-11-24*
+
+Security
+--------
+
+- bpo-42103: Prevented potential DoS attack via CPU and RAM exhaustion when
+  processing malformed Apple Property List files in binary format.
+
+- bpo-42051: The :mod:`plistlib` module no longer accepts entity
+  declarations in XML plist files to avoid XML vulnerabilities. This should
+  not affect users as entity declarations are not used in regular plist
+  files.
+
+- bpo-40791: Add ``volatile`` to the accumulator variable in
+  ``hmac.compare_digest``, making constant-time-defeating optimizations less
+  likely.
+
+Core and Builtins
+-----------------
+
+- bpo-41686: On Windows, the ``SIGINT`` event, ``_PyOS_SigintEvent()``, is
+  now created even if Python is configured to not install signal handlers
+  (if :c:member:`PyConfig.install_signal_handlers` equals to 0, or
+  ``Py_InitializeEx(0)``).
+
+- bpo-42381: Allow assignment expressions in set literals and set
+  comprehensions as per PEP 572. Patch by Pablo Galindo.
+
+- bpo-42374: Fix a regression introduced by the new parser, where an
+  unparenthesized walrus operator was not allowed within generator
+  expressions.
+
+- bpo-42296: On Windows, fix a regression in signal handling which prevented
+  to interrupt a program using CTRL+C. The signal handler can be run in a
+  thread different than the Python thread, in which case the test deciding
+  if the thread can handle signals is wrong.
+
+- bpo-42332: :class:`types.GenericAlias` objects can now be the targets of
+  weakrefs.
+
+- bpo-42218: Fixed a bug in the PEG parser that was causing crashes in debug
+  mode. Now errors are checked in left-recursive rules to avoid cases where
+  such errors do not get handled in time and appear as long-distance crashes
+  in other places.
+
+- bpo-42214: Fixed a possible crash in the PEG parser when checking for the
+  '!=' token in the ``barry_as_flufl`` rule. Patch by Pablo Galindo.
+
+- bpo-42143: Fix handling of errors during creation of ``PyFunctionObject``,
+  which resulted in operations on uninitialized memory. Patch by Yonatan
+  Goldschmidt.
+
+- bpo-41659: Fix a bug in the parser, where a curly brace following a
+  `primary` didn't fail immediately. This led to invalid expressions like `a
+  {b}` to throw a :exc:`SyntaxError` with a wrong offset, or invalid
+  expressions ending with a curly brace like `a {` to not fail immediately
+  in the REPL.
+
+- bpo-42150: Fix possible buffer overflow in the new parser when checking
+  for continuation lines. Patch by Pablo Galindo.
+
+- bpo-42123: Run the parser two times. On the first run, disable all the
+  rules that only generate better error messages to gain performance. If
+  there's a parse failure, run the parser a second time with those enabled.
+
+- bpo-41910: Document the default implementation of `object.__eq__`.
+
+- bpo-42057: Fix peephole optimizer misoptimize conditional jump +
+  JUMP_IF_NOT_EXC_MATCH pair.
+
+- bpo-41984: The garbage collector now tracks all user-defined classes.
+  Patch by Brandt Bucher.
+
+- bpo-41993: Fixed potential issues with removing not completely initialized
+  module from ``sys.modules`` when import fails.
+
+- bpo-41979: Star-unpacking is now allowed for with item's targets in the
+  PEG parser.
+
+- bpo-41909: Fixed stack overflow in :func:`issubclass` and
+  :func:`isinstance` when getting the ``__bases__`` attribute leads to
+  infinite recursion.
+
+- bpo-41894: When loading a native module and a load failure occurs, prevent
+  a possible UnicodeDecodeError when not running in a UTF-8 locale by
+  decoding the load error message using the current locale's encoding.
+
+- bpo-39934: Correctly count control blocks in 'except' in compiler. Ensures
+  that a syntax error, rather a fatal error, occurs for deeply nested, named
+  exception handlers.
+
+Library
+-------
+
+- bpo-42328: Fixed :meth:`tkinter.ttk.Style.map`. The function accepts now
+  the representation of the default state as empty sequence (as returned by
+  ``Style.map()``). The structure of the result is now the same on all
+  platform and does not depend on the value of ``wantobjects``.
+
+- bpo-42345: Fix various issues with ``typing.Literal`` parameter handling
+  (flatten, deduplicate, use type to cache key). Patch provided by Yurii
+  Karabas.
+
+- bpo-42350: Fix the :class:`threading.Thread` class at fork: do nothing if
+  the thread is already stopped (ex: fork called at Python exit).
+  Previously, an error was logged in the child process.
+
+- bpo-42014: The ``onerror`` callback from ``shutil.rmtree`` now receives
+  correct function when ``os.open`` fails.
+
+- bpo-42237: Fix `os.sendfile()` on illumos.
+
+- bpo-42249: Fixed writing binary Plist files larger than 4 GiB.
+
+- bpo-35455: On Solaris, :func:`~time.thread_time` is now implemented with
+  ``gethrvtime()`` because ``clock_gettime(CLOCK_THREAD_CPUTIME_ID)`` is not
+  always available. Patch by Jakub Kulik.
+
+- bpo-42233: The :func:`repr` of :mod:`typing` types containing
+  :ref:`Generic Alias Types <types-genericalias>` previously did not show
+  the parameterized types in the ``GenericAlias``.  They have now been
+  changed to do so.
+
+- bpo-41754: webbrowser: Ignore *NotADirectoryError* when calling
+  ``xdg-settings``.
+
+- bpo-29566: ``binhex.binhex()`` consisently writes macOS 9 line endings.
+
+- bpo-42183: Fix a stack overflow error for asyncio Task or Future repr().
+
+  The overflow occurs under some circumstances when a Task or Future
+  recursively returns itself.
+
+- bpo-42146: Fix memory leak in :func:`subprocess.Popen` in case an uid
+  (gid) specified in `user` (`group`, `extra_groups`) overflows `uid_t`
+  (`gid_t`).
+
+- bpo-42140: Improve asyncio.wait function to create the futures set just
+  one time.
+
+- bpo-42103: :exc:`~plistlib.InvalidFileException` and :exc:`RecursionError`
+  are now the only errors caused by loading malformed binary Plist file
+  (previously ValueError and TypeError could be raised in some specific
+  cases).
+
+- bpo-41052: Pickling heap types implemented in C with protocols 0 and 1
+  raises now an error instead of producing incorrect data.
+
+- bpo-41491: plistlib: fix parsing XML plists with hexadecimal integer
+  values
+
+- bpo-42065: Fix an incorrectly formatted error from
+  :meth:`_codecs.charmap_decode` when called with a mapped value outside the
+  range of valid Unicode code points. PR by Max Bernstein.
+
+- bpo-41966: Fix pickling pure Python :class:`datetime.time` subclasses.
+  Patch by Dean Inwood.
+
+- bpo-41976: Fixed a bug that was causing :func:`ctypes.util.find_library`
+  to return ``None`` when triying to locate a library in an environment when
+  gcc>=9 is available and ``ldconfig`` is not. Patch by Pablo Galindo
+
+- bpo-41900: C14N 2.0 serialisation in xml.etree.ElementTree failed for
+  unprefixed attributes when a default namespace was defined.
+
+- bpo-41840: Fix a bug in the :mod:`symtable` module that was causing
+  module-scope global variables to not be reported as both local and global.
+  Patch by Pablo Galindo.
+
+- bpo-41831: ``str()`` for the ``type`` attribute of the ``tkinter.Event``
+  object always returns now the numeric code returned by Tk instead of the
+  name of the event type.
+
+- bpo-41817: fix `tkinter.EventType` Enum so all members are strings, and
+  none are tuples
+
+- bpo-41815: Fix SQLite3 segfault when backing up closed database. Patch
+  contributed by Peter David McCormick.
+
+- bpo-41316: Fix the :mod:`tarfile` module to write only basename of TAR
+  file to GZIP compression header.
+
+- bpo-16936: Allow ``ctypes.wintypes`` to be imported on non-Windows
+  systems.
+
+- bpo-40592: :func:`shutil.which` now ignores empty entries in
+  :envvar:`PATHEXT` instead of treating them as a match.
+
+- bpo-40550: Fix time-of-check/time-of-action issue in
+  subprocess.Popen.send_signal.
+
+- bpo-40492: Fix ``--outfile`` for :mod:`cProfile` / :mod:`profile` not
+  writing the output file in the original directory when the program being
+  profiled changes the working directory.  PR by Anthony Sottile.
+
+- bpo-40105: ZipFile truncates files to avoid corruption when a shorter
+  comment is provided in append ("a") mode. Patch by Jan Mazur.
+
+- bpo-27321: Fixed KeyError exception when flattening an email to a string
+  attempts to replace a non-existent Content-Transfer-Encoding header.
+
+Documentation
+-------------
+
+- bpo-42153: Fix the URL for the IMAP protocol documents.
+
+- bpo-42061: Document __format__ functionality for IP addresses.
+
+- bpo-42010: Clarify that subscription expressions are also valid for
+  certain :term:`classes <class>` and :term:`types <type>` in the standard
+  library, and for user-defined classes and types if the classmethod
+  :meth:`__class_getitem__` is provided.
+
+- bpo-41805: Documented :ref:`generic alias type <types-genericalias>` and
+  :data:`types.GenericAlias`. Also added an entry in glossary for
+  :term:`generic types <generic type>`.
+
+- bpo-41774: In Programming FAQ "Sequences (Tuples/Lists)" section, add "How
+  do you remove multiple items from a list".
+
+- bpo-35293: Fix RemovedInSphinx40Warning when building the documentation.
+  Patch by Dong-hee Na.
+
+- bpo-41726: Update the refcounts info of ``PyType_FromModuleAndSpec``.
+
+- bpo-39693: Fix tarfile's extractfile documentation
+
+- bpo-39416: Document some restrictions on the default string
+  representations of numeric classes.
+
+Tests
+-----
+
+- bpo-40754: Include ``_testinternalcapi`` module in Windows installer for
+  test suite
+
+- bpo-41739: Fix test_logging.test_race_between_set_target_and_flush(): the
+  test now waits until all threads complete to avoid leaking running
+  threads.
+
+- bpo-41970: Avoid a test failure in ``test_lib2to3`` if the module has
+  already imported at the time the test executes. Patch by Pablo Galindo.
+
+- bpo-41944: Tests for CJK codecs no longer call ``eval()`` on content
+  received via HTTP.
+
+- bpo-41939: Fix test_site.test_license_exists_at_url(): call
+  ``urllib.request.urlcleanup()`` to reset the global
+  ``urllib.request._opener``. Patch by Victor Stinner.
+
+- bpo-41561: test_ssl: skip test_min_max_version_mismatch when TLS 1.0 is
+  not available
+
+- bpo-41602: Add tests for SIGINT handling in the runpy module.
+
+- bpo-41306: Fixed a failure in ``test_tk.test_widgets.ScaleTest`` happening
+  when executing the test with Tk 8.6.10.
+
+Build
+-----
+
+- bpo-42398: Fix a race condition in "make regen-all" when make -jN option
+  is used to run jobs in parallel. The clinic.py script now only use atomic
+  write to write files. Moveover, generated files are now left unchanged if
+  the content does not change, to not change the file modification time.
+
+- bpo-41617: Fix building ``pycore_bitutils.h`` internal header on old clang
+  version without ``__builtin_bswap16()`` (ex: Xcode 4.6.3 on Mac OS X
+  10.7). Patch by Joshua Root and Victor Stinner.
+
+- bpo-38249: Update :c:macro:`Py_UNREACHABLE` to use __builtin_unreachable()
+  if only the compiler is able to use it. Patch by Dong-hee Na.
+
+- bpo-40998: Addressed three compiler warnings found by undefined behavior
+  sanitizer (ubsan).
+
+Windows
+-------
+
+- bpo-42120: Remove macro definition of ``copysign`` (to ``_copysign``) in
+  headers.
+
+- bpo-38439: Updates the icons for IDLE in the Windows Store package.
+
+- bpo-41744: Fixes automatic import of props file when using the Nuget
+  package.
+
+- bpo-41557: Update Windows installer to use SQLite 3.33.0.
+
+- bpo-38324: Avoid Unicode errors when accessing certain locale data on
+  Windows.
+
+macOS
+-----
+
+- bpo-41116: Ensure distutils.unixxcompiler.find_library_file can find
+  system provided libraries on macOS 11.
+
+- bpo-41100: Add support for macOS 11 and Apple Silicon systems.
+
+  It is now possible to build "Universal 2" binaries using
+  "--enable-universalsdk --with-universal-archs=universal2".
+
+  Binaries build on later macOS versions can be deployed back to older
+  versions (tested up to macOS 10.9), when using the correct deployment
+  target. This is tested using Xcode 11 and later.
+
+- bpo-38443: The ``--enable-universalsdk`` and ``--with-universal-archs``
+  options for the configure script now check that the specified
+  architectures can be used.
+
+- bpo-41471: Ignore invalid prefix lengths in system proxy excludes.
+
+- bpo-41557: Update macOS installer to use SQLite 3.33.0.
+
+IDLE
+----
+
+- bpo-42426: Fix reporting offset of the RE error in searchengine.
+
+- bpo-42415: Get docstrings for IDLE calltips more often by using
+  inspect.getdoc.
+
+- bpo-33987: Mostly finish using ttk widgets, mainly for editor, settings,
+  and searches. Some patches by Mark Roseman.
+
+- bpo-41775: Use 'IDLE Shell' as shell title
+
+- bpo-35764: Rewrite the Calltips doc section.
+
+- bpo-40181: In calltips, stop reminding that '/' marks the end of
+  positional-only arguments.
+
+- bpo-40511: Typing opening and closing parentheses inside the parentheses
+  of a function call will no longer cause unnecessary "flashing" off and on
+  of an existing open call-tip, e.g. when typed in a string literal.
+
+- bpo-38439: Add a 256×256 pixel IDLE icon to the Windows .ico file. Created
+  by Andrew Clover. Remove the low-color gif variations from the .ico file.
+
+C API
+-----
+
+- bpo-42015: Fix potential crash in deallocating method objects when
+  dynamically allocated `PyMethodDef`'s lifetime is managed through the
+  ``self`` argument of a `PyCFunction`.
+
+- bpo-41986: :c:data:`Py_FileSystemDefaultEncodeErrors` and
+  :c:data:`Py_UTF8Mode` are available again in limited API.
+
+
 What's New in Python 3.9.0 final?
 =================================
 
@@ -2907,10 +3339,10 @@ Core and Builtins
 
 - bpo-33387: Removed WITH_CLEANUP_START, WITH_CLEANUP_FINISH, BEGIN_FINALLY,
   END_FINALLY, CALL_FINALLY and POP_FINALLY bytecodes. Replaced with RERAISE
-  and WITH_EXCEPT_FINISH bytecodes. The compiler now generates different
-  code for exceptional and non-exceptional branches for 'with' and
-  'try-except' statements. For 'try-finally' statements the 'finally' block
-  is replicated for each exit from the 'try' body.
+  and WITH_EXCEPT_START bytecodes. The compiler now generates different code
+  for exceptional and non-exceptional branches for 'with' and 'try-except'
+  statements. For 'try-finally' statements the 'finally' block is replicated
+  for each exit from the 'try' body.
 
 Library
 -------
index 2abfa67..19c77f4 100644 (file)
@@ -1,6 +1,8 @@
 #include "Python.h"
 #include "frameobject.h"
 
+#include <stdbool.h>
+
 #include <ffi.h>
 #ifdef MS_WIN32
 #include <windows.h>
@@ -18,7 +20,7 @@ CThunkObject_dealloc(PyObject *myself)
     Py_XDECREF(self->callable);
     Py_XDECREF(self->restype);
     if (self->pcl_write)
-        ffi_closure_free(self->pcl_write);
+        Py_ffi_closure_free(self->pcl_write);
     PyObject_GC_Del(self);
 }
 
@@ -361,8 +363,7 @@ CThunkObject *_ctypes_alloc_callback(PyObject *callable,
 
     assert(CThunk_CheckExact((PyObject *)p));
 
-    p->pcl_write = ffi_closure_alloc(sizeof(ffi_closure),
-                                                                         &p->pcl_exec);
+    p->pcl_write = Py_ffi_closure_alloc(sizeof(ffi_closure), &p->pcl_exec);
     if (p->pcl_write == NULL) {
         PyErr_NoMemory();
         goto error;
@@ -408,13 +409,42 @@ CThunkObject *_ctypes_alloc_callback(PyObject *callable,
                      "ffi_prep_cif failed with %d", result);
         goto error;
     }
-#if defined(X86_DARWIN) || defined(POWERPC_DARWIN)
-    result = ffi_prep_closure(p->pcl_write, &p->cif, closure_fcn, p);
+#if HAVE_FFI_PREP_CLOSURE_LOC
+#   if USING_APPLE_OS_LIBFFI
+#      define HAVE_FFI_PREP_CLOSURE_LOC_RUNTIME __builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)
+#   else
+#      define HAVE_FFI_PREP_CLOSURE_LOC_RUNTIME 1
+#   endif
+    if (HAVE_FFI_PREP_CLOSURE_LOC_RUNTIME) {
+        result = ffi_prep_closure_loc(p->pcl_write, &p->cif, closure_fcn,
+                                    p,
+                                    p->pcl_exec);
+    } else
+#endif
+    {
+#if USING_APPLE_OS_LIBFFI && defined(__arm64__)
+        PyErr_Format(PyExc_NotImplementedError, "ffi_prep_closure_loc() is missing");
+        goto error;
 #else
-    result = ffi_prep_closure_loc(p->pcl_write, &p->cif, closure_fcn,
-                                  p,
-                                  p->pcl_exec);
+#if defined(__clang__) || defined(MACOSX)
+        #pragma clang diagnostic push
+        #pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#endif
+#if defined(__GNUC__)
+        #pragma GCC diagnostic push
+        #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 #endif
+        result = ffi_prep_closure(p->pcl_write, &p->cif, closure_fcn, p);
+
+#if defined(__clang__) || defined(MACOSX)
+        #pragma clang diagnostic pop
+#endif
+#if defined(__GNUC__)
+        #pragma GCC diagnostic pop
+#endif
+
+#endif
+    }
     if (result != FFI_OK) {
         PyErr_Format(PyExc_RuntimeError,
                      "ffi_prep_closure failed with %d", result);
index 6030cc3..b0a36a3 100644 (file)
@@ -57,6 +57,8 @@
 #include "Python.h"
 #include "structmember.h"         // PyMemberDef
 
+#include <stdbool.h>
+
 #ifdef MS_WIN32
 #include <windows.h>
 #include <tchar.h>
 #include "ctypes_dlfcn.h"
 #endif
 
+#ifdef __APPLE__
+#include <mach-o/dyld.h>
+#endif
+
 #ifdef MS_WIN32
 #include <malloc.h>
 #endif
@@ -812,7 +818,8 @@ static int _call_function_pointer(int flags,
                                   ffi_type **atypes,
                                   ffi_type *restype,
                                   void *resmem,
-                                  int argcount)
+                                  int argcount,
+                                  int argtypecount)
 {
     PyThreadState *_save = NULL; /* For Py_BLOCK_THREADS and Py_UNBLOCK_THREADS */
     PyObject *error_object = NULL;
@@ -835,14 +842,70 @@ static int _call_function_pointer(int flags,
     if ((flags & FUNCFLAG_CDECL) == 0)
         cc = FFI_STDCALL;
 #endif
-    if (FFI_OK != ffi_prep_cif(&cif,
-                               cc,
-                               argcount,
-                               restype,
-                               atypes)) {
-        PyErr_SetString(PyExc_RuntimeError,
-                        "ffi_prep_cif failed");
-        return -1;
+
+#   if USING_APPLE_OS_LIBFFI
+#      define HAVE_FFI_PREP_CIF_VAR_RUNTIME __builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)
+#   elif HAVE_FFI_PREP_CIF_VAR
+#      define HAVE_FFI_PREP_CIF_VAR_RUNTIME true
+#   else
+#      define HAVE_FFI_PREP_CIF_VAR_RUNTIME false
+#   endif
+
+    /* Even on Apple-arm64 the calling convention for variadic functions conincides
+     * with the standard calling convention in the case that the function called
+     * only with its fixed arguments.   Thus, we do not need a special flag to be
+     * set on variadic functions.   We treat a function as variadic if it is called
+     * with a nonzero number of variadic arguments */
+    bool is_variadic = (argtypecount != 0 && argcount > argtypecount);
+    (void) is_variadic;
+
+#if defined(__APPLE__) && defined(__arm64__)
+    if (is_variadic) {
+        if (HAVE_FFI_PREP_CIF_VAR_RUNTIME) {
+        } else {
+            PyErr_SetString(PyExc_NotImplementedError, "ffi_prep_cif_var() is missing");
+            return -1;
+        }
+    }
+#endif
+
+#if HAVE_FFI_PREP_CIF_VAR
+    if (is_variadic) {
+        if (HAVE_FFI_PREP_CIF_VAR_RUNTIME) {
+            if (FFI_OK != ffi_prep_cif_var(&cif,
+                                        cc,
+                                        argtypecount,
+                                        argcount,
+                                        restype,
+                                        atypes)) {
+                PyErr_SetString(PyExc_RuntimeError,
+                                "ffi_prep_cif_var failed");
+                return -1;
+            }
+        } else {
+            if (FFI_OK != ffi_prep_cif(&cif,
+                                       cc,
+                                       argcount,
+                                       restype,
+                                       atypes)) {
+                PyErr_SetString(PyExc_RuntimeError,
+                                "ffi_prep_cif failed");
+                return -1;
+            }
+        }
+    } else
+#endif
+
+    {
+        if (FFI_OK != ffi_prep_cif(&cif,
+                                   cc,
+                                   argcount,
+                                   restype,
+                                   atypes)) {
+            PyErr_SetString(PyExc_RuntimeError,
+                            "ffi_prep_cif failed");
+            return -1;
+        }
     }
 
     if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) {
@@ -1212,9 +1275,8 @@ PyObject *_ctypes_callproc(PPROC pProc,
 
     if (-1 == _call_function_pointer(flags, pProc, avalues, atypes,
                                      rtype, resbuf,
-                                     Py_SAFE_DOWNCAST(argcount,
-                                                      Py_ssize_t,
-                                                      int)))
+                                     Py_SAFE_DOWNCAST(argcount, Py_ssize_t, int),
+                                     Py_SAFE_DOWNCAST(argtype_count, Py_ssize_t, int)))
         goto cleanup;
 
 #ifdef WORDS_BIGENDIAN
@@ -1398,6 +1460,42 @@ copy_com_pointer(PyObject *self, PyObject *args)
 }
 #else
 
+#ifdef HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH
+static PyObject *py_dyld_shared_cache_contains_path(PyObject *self, PyObject *args)
+{
+     PyObject *name, *name2;
+     char *name_str;
+
+     if (__builtin_available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *)) {
+         int r;
+
+         if (!PyArg_ParseTuple(args, "O", &name))
+             return NULL;
+    
+         if (name == Py_None)
+             Py_RETURN_FALSE;
+    
+         if (PyUnicode_FSConverter(name, &name2) == 0)
+             return NULL;
+         name_str = PyBytes_AS_STRING(name2);
+    
+         r = _dyld_shared_cache_contains_path(name_str);
+         Py_DECREF(name2);
+
+         if (r) {
+             Py_RETURN_TRUE;
+         } else {
+             Py_RETURN_FALSE;
+         }
+
+     } else {
+         PyErr_SetString(PyExc_NotImplementedError, "_dyld_shared_cache_contains_path symbol is missing");
+         return NULL;
+     }
+
+ }
+#endif
+
 static PyObject *py_dl_open(PyObject *self, PyObject *args)
 {
     PyObject *name, *name2;
@@ -1887,6 +1985,8 @@ buffer_info(PyObject *self, PyObject *arg)
     return Py_BuildValue("siN", dict->format, dict->ndim, shape);
 }
 
+
+
 PyMethodDef _ctypes_module_methods[] = {
     {"get_errno", get_errno, METH_NOARGS},
     {"set_errno", set_errno, METH_VARARGS},
@@ -1909,6 +2009,9 @@ PyMethodDef _ctypes_module_methods[] = {
     {"dlclose", py_dl_close, METH_VARARGS, "dlclose a library"},
     {"dlsym", py_dl_sym, METH_VARARGS, "find symbol in shared library"},
 #endif
+#ifdef HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH
+     {"_dyld_shared_cache_contains_path", py_dyld_shared_cache_contains_path, METH_VARARGS, "check if path is in the shared cache"},
+#endif
     {"alignment", align_func, METH_O, alignment_doc},
     {"sizeof", sizeof_func, METH_O, sizeof_doc},
     {"byref", byref, METH_VARARGS, byref_doc},
index a72682d..2261a59 100644 (file)
@@ -684,7 +684,11 @@ i_get_sw(void *ptr, Py_ssize_t size)
     return PyLong_FromLong(val);
 }
 
-#ifdef MS_WIN32
+#ifndef MS_WIN32
+/* http://msdn.microsoft.com/en-us/library/cc237864.aspx */
+#define VARIANT_FALSE 0x0000
+#define VARIANT_TRUE 0xFFFF
+#endif
 /* short BOOL - VARIANT_BOOL */
 static PyObject *
 vBOOL_set(void *ptr, PyObject *value, Py_ssize_t size)
@@ -706,7 +710,6 @@ vBOOL_get(void *ptr, Py_ssize_t size)
 {
     return PyBool_FromLong((long)*(short int *)ptr);
 }
-#endif
 
 static PyObject *
 bool_set(void *ptr, PyObject *value, Py_ssize_t size)
@@ -1538,8 +1541,8 @@ static struct fielddesc formattable[] = {
 #endif
 #ifdef MS_WIN32
     { 'X', BSTR_set, BSTR_get, &ffi_type_pointer},
-    { 'v', vBOOL_set, vBOOL_get, &ffi_type_sshort},
 #endif
+    { 'v', vBOOL_set, vBOOL_get, &ffi_type_sshort},
 #if SIZEOF__BOOL == 1
     { '?', bool_set, bool_get, &ffi_type_uchar}, /* Also fallback for no native _Bool support */
 #elif SIZEOF__BOOL == SIZEOF_SHORT
index 1effccf..3f20031 100644 (file)
@@ -366,6 +366,14 @@ PyObject *_ctypes_get_errobj(int **pspace);
 extern PyObject *ComError;
 #endif
 
+#if USING_MALLOC_CLOSURE_DOT_C
+void Py_ffi_closure_free(void *p);
+void *Py_ffi_closure_alloc(size_t size, void** codeloc);
+#else
+#define Py_ffi_closure_free ffi_closure_free
+#define Py_ffi_closure_alloc ffi_closure_alloc
+#endif
+
 /*
  Local Variables:
  compile-command: "python setup.py -q build install --home ~"
index f9cdb33..4f220e4 100644 (file)
@@ -89,16 +89,27 @@ static void more_core(void)
 /******************************************************************/
 
 /* put the item back into the free list */
-void ffi_closure_free(void *p)
+void Py_ffi_closure_free(void *p)
 {
+#if USING_APPLE_OS_LIBFFI && HAVE_FFI_CLOSURE_ALLOC
+    if (__builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)) {
+        ffi_closure_free(p);
+        return;
+    }
+#endif
     ITEM *item = (ITEM *)p;
     item->next = free_list;
     free_list = item;
 }
 
 /* return one item from the free list, allocating more if needed */
-void *ffi_closure_alloc(size_t ignored, void** codeloc)
+void *Py_ffi_closure_alloc(size_t size, void** codeloc)
 {
+#if USING_APPLE_OS_LIBFFI && HAVE_FFI_CLOSURE_ALLOC
+    if (__builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)) {
+        return ffi_closure_alloc(size, codeloc);
+    }
+#endif
     ITEM *item;
     if (!free_list)
         more_core();
index 2815a8c..5a24396 100644 (file)
@@ -121,6 +121,9 @@ const char *mpd_version(void);
   #elif defined(__x86_64__)
     #define CONFIG_64
     #define ASM
+  #elif defined(__arm64__)
+    #define CONFIG_64
+    #define ANSI
   #else
     #error "unknown architecture for universal build."
   #endif
index 0819d0e..2e353bb 100644 (file)
@@ -144,6 +144,7 @@ locale_is_ascii(const char *str)
 static int
 locale_decode_monetary(PyObject *dict, struct lconv *lc)
 {
+#ifndef MS_WINDOWS
     int change_locale;
     change_locale = (!locale_is_ascii(lc->int_curr_symbol)
                      || !locale_is_ascii(lc->currency_symbol)
@@ -179,12 +180,18 @@ locale_decode_monetary(PyObject *dict, struct lconv *lc)
         }
     }
 
+#define GET_LOCALE_STRING(ATTR) PyUnicode_DecodeLocale(lc->ATTR, NULL)
+#else  /* MS_WINDOWS */
+/* Use _W_* fields of Windows struct lconv */
+#define GET_LOCALE_STRING(ATTR) PyUnicode_FromWideChar(lc->_W_ ## ATTR, -1)
+#endif /* MS_WINDOWS */
+
     int res = -1;
 
 #define RESULT_STRING(ATTR) \
     do { \
         PyObject *obj; \
-        obj = PyUnicode_DecodeLocale(lc->ATTR, NULL); \
+        obj = GET_LOCALE_STRING(ATTR); \
         if (obj == NULL) { \
             goto done; \
         } \
@@ -200,14 +207,17 @@ locale_decode_monetary(PyObject *dict, struct lconv *lc)
     RESULT_STRING(mon_decimal_point);
     RESULT_STRING(mon_thousands_sep);
 #undef RESULT_STRING
+#undef GET_LOCALE_STRING
 
     res = 0;
 
 done:
+#ifndef MS_WINDOWS
     if (loc != NULL) {
         setlocale(LC_CTYPE, oldloc);
     }
     PyMem_Free(oldloc);
+#endif
     return res;
 }
 
@@ -243,9 +253,15 @@ PyLocale_localeconv(PyObject* self, PyObject *Py_UNUSED(ignored))
         Py_DECREF(obj); \
     } while (0)
 
+#ifdef MS_WINDOWS
+/* Use _W_* fields of Windows struct lconv */
+#define GET_LOCALE_STRING(ATTR) PyUnicode_FromWideChar(lc->_W_ ## ATTR, -1)
+#else
+#define GET_LOCALE_STRING(ATTR) PyUnicode_DecodeLocale(lc->ATTR, NULL)
+#endif
 #define RESULT_STRING(s)\
     do { \
-        x = PyUnicode_DecodeLocale(lc->s, NULL); \
+        x = GET_LOCALE_STRING(s); \
         RESULT(#s, x); \
     } while (0)
 
@@ -274,8 +290,10 @@ PyLocale_localeconv(PyObject* self, PyObject *Py_UNUSED(ignored))
     RESULT_INT(n_sign_posn);
 
     /* Numeric information: LC_NUMERIC encoding */
-    PyObject *decimal_point, *thousands_sep;
+    PyObject *decimal_point = NULL, *thousands_sep = NULL;
     if (_Py_GetLocaleconvNumeric(lc, &decimal_point, &thousands_sep) < 0) {
+        Py_XDECREF(decimal_point);
+        Py_XDECREF(thousands_sep);
         goto failed;
     }
 
@@ -304,6 +322,7 @@ PyLocale_localeconv(PyObject* self, PyObject *Py_UNUSED(ignored))
 #undef RESULT
 #undef RESULT_STRING
 #undef RESULT_INT
+#undef GET_LOCALE_STRING
 }
 
 #if defined(HAVE_WCSCOLL)
index 8a54829..6f8f68f 100644 (file)
@@ -735,7 +735,7 @@ _tscmp(const unsigned char *a, const unsigned char *b,
     volatile const unsigned char *left;
     volatile const unsigned char *right;
     Py_ssize_t i;
-    unsigned char result;
+    volatile unsigned char result;
 
     /* loop count depends on length of b */
     length = len_b;
index 5d1691a..5356417 100644 (file)
@@ -626,7 +626,7 @@ subprocess_fork_exec(PyObject* self, PyObject *args)
     uid_t uid;
     gid_t gid, *groups = NULL;
     int child_umask;
-    PyObject *cwd_obj, *cwd_obj2;
+    PyObject *cwd_obj, *cwd_obj2 = NULL;
     const char *cwd;
     pid_t pid;
     int need_to_reenable_gc = 0;
@@ -748,7 +748,6 @@ subprocess_fork_exec(PyObject* self, PyObject *args)
         cwd = PyBytes_AsString(cwd_obj2);
     } else {
         cwd = NULL;
-        cwd_obj2 = NULL;
     }
 
     if (groups_list != Py_None) {
@@ -908,6 +907,7 @@ subprocess_fork_exec(PyObject* self, PyObject *args)
     return PyLong_FromPid(pid);
 
 cleanup:
+    Py_XDECREF(cwd_obj2);
     if (envp)
         _Py_FreeCharPArray(envp);
     if (argv)
index 1b01491..a402b93 100644 (file)
@@ -537,29 +537,12 @@ random_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 }
 
 
-/*[clinic input]
-
-_random.Random.__reduce__
-
-[clinic start generated code]*/
-
-static PyObject *
-_random_Random___reduce___impl(RandomObject *self)
-/*[clinic end generated code: output=ddea0dcdb60ffd6d input=bd38ec35fd157e0f]*/
-{
-    PyErr_Format(PyExc_TypeError,
-                 "cannot pickle %s object",
-                 Py_TYPE(self)->tp_name);
-    return NULL;
-}
-
 static PyMethodDef random_methods[] = {
     _RANDOM_RANDOM_RANDOM_METHODDEF
     _RANDOM_RANDOM_SEED_METHODDEF
     _RANDOM_RANDOM_GETSTATE_METHODDEF
     _RANDOM_RANDOM_SETSTATE_METHODDEF
     _RANDOM_RANDOM_GETRANDBITS_METHODDEF
-    _RANDOM_RANDOM___REDUCE___METHODDEF
     {NULL,              NULL}           /* sentinel */
 };
 
index 28796b3..7bdde45 100644 (file)
@@ -899,6 +899,7 @@ _ssl_configure_hostname(PySSLSocket *self, const char* server_hostname)
     if (ip == NULL) {
         if (!SSL_set_tlsext_host_name(self->ssl, server_hostname)) {
             _setSSLError(NULL, 0, __FILE__, __LINE__);
+            goto error;
         }
     }
     if (self->ctx->check_hostname) {
index b2d070c..54c1e62 100644 (file)
@@ -3629,6 +3629,25 @@ with_tp_del(PyObject *self, PyObject *args)
     return obj;
 }
 
+static PyObject *
+without_gc(PyObject *Py_UNUSED(self), PyObject *obj)
+{
+    PyTypeObject *tp = (PyTypeObject*)obj;
+    if (!PyType_Check(obj) || !PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)) {
+        return PyErr_Format(PyExc_TypeError, "heap type expected, got %R", obj);
+    }
+    if (PyType_IS_GC(tp)) {
+        // Don't try this at home, kids:
+        tp->tp_flags -= Py_TPFLAGS_HAVE_GC;
+        tp->tp_free = PyObject_Del;
+        tp->tp_traverse = NULL;
+        tp->tp_clear = NULL;
+    }
+    assert(!PyType_IS_GC(tp));
+    Py_INCREF(obj);
+    return obj;
+}
+
 static PyMethodDef ml;
 
 static PyObject *
@@ -5535,6 +5554,7 @@ static PyMethodDef TestMethods[] = {
     {"meth_fastcall", (PyCFunction)(void(*)(void))meth_fastcall, METH_FASTCALL},
     {"meth_fastcall_keywords", (PyCFunction)(void(*)(void))meth_fastcall_keywords, METH_FASTCALL|METH_KEYWORDS},
     {"pynumber_tobase", pynumber_tobase, METH_VARARGS},
+    {"without_gc", without_gc, METH_O},
     {NULL, NULL} /* sentinel */
 };
 
index fc91622..04f6c24 100644 (file)
@@ -1199,7 +1199,7 @@ tracemalloc_copy_trace(_Py_hashtable_t *traces,
     trace_t *trace = (trace_t *)value;
 
     trace_t *trace2 = raw_malloc(sizeof(trace_t));
-    if (traces2 == NULL) {
+    if (trace2 == NULL) {
         return -1;
     }
     *trace2 = *trace;
index 0a642df..a467811 100644 (file)
@@ -114,21 +114,4 @@ _random_Random_getrandbits(RandomObject *self, PyObject *arg)
 exit:
     return return_value;
 }
-
-PyDoc_STRVAR(_random_Random___reduce____doc__,
-"__reduce__($self, /)\n"
-"--\n"
-"\n");
-
-#define _RANDOM_RANDOM___REDUCE___METHODDEF    \
-    {"__reduce__", (PyCFunction)_random_Random___reduce__, METH_NOARGS, _random_Random___reduce____doc__},
-
-static PyObject *
-_random_Random___reduce___impl(RandomObject *self);
-
-static PyObject *
-_random_Random___reduce__(RandomObject *self, PyObject *Py_UNUSED(ignored))
-{
-    return _random_Random___reduce___impl(self);
-}
-/*[clinic end generated code: output=d8a99be3f1192219 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=a7feb0c9c8d1b627 input=a9049054013a1b77]*/
index a84c858..4035819 100644 (file)
@@ -923,11 +923,7 @@ static PyStatus
 calculate_program_macos(wchar_t **abs_path_p)
 {
     char execpath[MAXPATHLEN + 1];
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
     uint32_t nsexeclength = Py_ARRAY_LENGTH(execpath) - 1;
-#else
-    unsigned long nsexeclength = Py_ARRAY_LENGTH(execpath) - 1;
-#endif
 
     /* On Mac OS X, if a script uses an interpreter of the form
        "#!/opt/python2.3/bin/python", the kernel only passes "python"
index 01e8bcb..12f72f5 100644 (file)
@@ -7,18 +7,6 @@
    of the compiler used.  Different compilers define their own feature
    test macro, e.g. '_MSC_VER'. */
 
-#ifdef __APPLE__
-   /*
-    * Step 1 of support for weak-linking a number of symbols existing on
-    * OSX 10.4 and later, see the comment in the #ifdef __APPLE__ block
-    * at the end of this file for more information.
-    */
-#  pragma weak lchown
-#  pragma weak statvfs
-#  pragma weak fstatvfs
-
-#endif /* __APPLE__ */
-
 #define PY_SSIZE_T_CLEAN
 
 #include "Python.h"
 
 #include <stdio.h>  /* needed for ctermid() */
 
+/*
+ * A number of APIs are available on macOS from a certain macOS version.
+ * To support building with a new SDK while deploying to older versions
+ * the availability test is split into two:
+ *   - HAVE_<FUNCTION>:  The configure check for compile time availability
+ *   - HAVE_<FUNCTION>_RUNTIME: Runtime check for availability
+ *
+ * The latter is always true when not on macOS, or when using a compiler
+ * that does not support __has_builtin (older versions of Xcode).
+ *
+ * Due to compiler restrictions there is one valid use of HAVE_<FUNCTION>_RUNTIME:
+ *    if (HAVE_<FUNCTION>_RUNTIME) { ... }
+ *
+ * In mixing the test with other tests or using negations will result in compile
+ * errors.
+ */
+#if defined(__APPLE__)
+
+#if defined(__has_builtin) && __has_builtin(__builtin_available)
+#  define HAVE_FSTATAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+#  define HAVE_FACCESSAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+#  define HAVE_FCHMODAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+#  define HAVE_FCHOWNAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+#  define HAVE_LINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+#  define HAVE_FDOPENDIR_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+#  define HAVE_MKDIRAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+#  define HAVE_RENAMEAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+#  define HAVE_UNLINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+#  define HAVE_OPENAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+#  define HAVE_READLINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+#  define HAVE_SYMLINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *)
+#  define HAVE_FUTIMENS_RUNTIME __builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)
+#  define HAVE_UTIMENSAT_RUNTIME __builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)
+#  define HAVE_PWRITEV_RUNTIME __builtin_available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *)
+
+#  define HAVE_POSIX_SPAWN_SETSID_RUNTIME __builtin_available(macOS 10.15, *)
+
+#else /* Xcode 8 or earlier */
+
+   /* __builtin_available is not present in these compilers, but
+    * some of the symbols might be weak linked (10.10 SDK or later
+    * deploying on 10.9.
+    *
+    * Fall back to the older style of availability checking for
+    * symbols introduced in macOS 10.10.
+    */
+
+#  ifdef HAVE_FSTATAT
+#    define HAVE_FSTATAT_RUNTIME (fstatat != NULL)
+#  endif
+
+#  ifdef HAVE_FACCESSAT
+#    define HAVE_FACCESSAT_RUNTIME (faccessat != NULL)
+#  endif
+
+#  ifdef HAVE_FCHMODAT
+#    define HAVE_FCHMODAT_RUNTIME (fchmodat != NULL)
+#  endif
+
+#  ifdef HAVE_FCHOWNAT
+#    define HAVE_FCHOWNAT_RUNTIME (fchownat != NULL)
+#  endif
+
+#  ifdef HAVE_LINKAT
+#    define HAVE_LINKAT_RUNTIME (linkat != NULL)
+#  endif
+
+#  ifdef HAVE_FDOPENDIR
+#    define HAVE_FDOPENDIR_RUNTIME (fdopendir != NULL)
+#  endif
+
+#  ifdef HAVE_MKDIRAT
+#    define HAVE_MKDIRAT_RUNTIME (mkdirat != NULL)
+#  endif
+
+#  ifdef HAVE_RENAMEAT
+#    define HAVE_RENAMEAT_RUNTIME (renameat != NULL)
+#  endif
+
+#  ifdef HAVE_UNLINKAT
+#    define HAVE_UNLINKAT_RUNTIME (unlinkat != NULL)
+#  endif
+
+#  ifdef HAVE_OPENAT
+#    define HAVE_OPENAT_RUNTIME (openat != NULL)
+#  endif
+
+#  ifdef HAVE_READLINKAT
+#    define HAVE_READLINKAT_RUNTIME (readlinkat != NULL)
+#  endif
+
+#  ifdef HAVE_SYMLINKAT
+#    define HAVE_SYMLINKAT_RUNTIME (symlinkat != NULL)
+#  endif
+
+#endif
+
+#ifdef HAVE_FUTIMESAT
+/* Some of the logic for weak linking depends on this assertion */
+# error "HAVE_FUTIMESAT unexpectedly defined"
+#endif
+
+#else
+#  define HAVE_FSTATAT_RUNTIME 1
+#  define HAVE_FACCESSAT_RUNTIME 1
+#  define HAVE_FCHMODAT_RUNTIME 1
+#  define HAVE_FCHOWNAT_RUNTIME 1
+#  define HAVE_LINKAT_RUNTIME 1
+#  define HAVE_FDOPENDIR_RUNTIME 1
+#  define HAVE_MKDIRAT_RUNTIME 1
+#  define HAVE_RENAMEAT_RUNTIME 1
+#  define HAVE_UNLINKAT_RUNTIME 1
+#  define HAVE_OPENAT_RUNTIME 1
+#  define HAVE_READLINKAT_RUNTIME 1
+#  define HAVE_SYMLINKAT_RUNTIME 1
+#  define HAVE_FUTIMENS_RUNTIME 1
+#  define HAVE_UTIMENSAT_RUNTIME 1
+#  define HAVE_PWRITEV_RUNTIME 1
+#endif
+
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -2308,6 +2417,10 @@ posix_do_stat(PyObject *module, const char *function_name, path_t *path,
     STRUCT_STAT st;
     int result;
 
+#ifdef HAVE_FSTATAT
+    int fstatat_unavailable = 0;
+#endif
+
 #if !defined(MS_WINDOWS) && !defined(HAVE_FSTATAT) && !defined(HAVE_LSTAT)
     if (follow_symlinks_specified(function_name, follow_symlinks))
         return NULL;
@@ -2334,15 +2447,27 @@ posix_do_stat(PyObject *module, const char *function_name, path_t *path,
     else
 #endif /* HAVE_LSTAT */
 #ifdef HAVE_FSTATAT
-    if ((dir_fd != DEFAULT_DIR_FD) || !follow_symlinks)
-        result = fstatat(dir_fd, path->narrow, &st,
+    if ((dir_fd != DEFAULT_DIR_FD) || !follow_symlinks) {
+        if (HAVE_FSTATAT_RUNTIME) {
+            result = fstatat(dir_fd, path->narrow, &st,
                          follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW);
-    else
+
+        } else {
+            fstatat_unavailable = 1;
+        }
+    } else
 #endif /* HAVE_FSTATAT */
         result = STAT(path->narrow, &st);
 #endif /* MS_WINDOWS */
     Py_END_ALLOW_THREADS
 
+#ifdef HAVE_FSTATAT
+    if (fstatat_unavailable) {
+        argument_unavailable_error("stat", "dir_fd");
+        return NULL;
+    }
+#endif
+
     if (result != 0) {
         return path_error(path);
     }
@@ -2760,6 +2885,10 @@ os_access_impl(PyObject *module, path_t *path, int mode, int dir_fd,
     int result;
 #endif
 
+#ifdef HAVE_FACCESSAT
+    int faccessat_unavailable = 0;
+#endif
+
 #ifndef HAVE_FACCESSAT
     if (follow_symlinks_specified("access", follow_symlinks))
         return -1;
@@ -2794,17 +2923,40 @@ os_access_impl(PyObject *module, path_t *path, int mode, int dir_fd,
     if ((dir_fd != DEFAULT_DIR_FD) ||
         effective_ids ||
         !follow_symlinks) {
-        int flags = 0;
-        if (!follow_symlinks)
-            flags |= AT_SYMLINK_NOFOLLOW;
-        if (effective_ids)
-            flags |= AT_EACCESS;
-        result = faccessat(dir_fd, path->narrow, mode, flags);
+
+        if (HAVE_FACCESSAT_RUNTIME) {
+            int flags = 0;
+            if (!follow_symlinks)
+                flags |= AT_SYMLINK_NOFOLLOW;
+            if (effective_ids)
+                flags |= AT_EACCESS;
+            result = faccessat(dir_fd, path->narrow, mode, flags);
+        } else {
+            faccessat_unavailable = 1;
+        }
     }
     else
 #endif
         result = access(path->narrow, mode);
     Py_END_ALLOW_THREADS
+
+#ifdef HAVE_FACCESSAT
+    if (faccessat_unavailable) {
+        if (dir_fd != DEFAULT_DIR_FD) {
+            argument_unavailable_error("access", "dir_fd");
+            return -1;
+        }
+        if (follow_symlinks_specified("access", follow_symlinks))
+            return -1;
+
+        if (effective_ids) {
+            argument_unavailable_error("access", "effective_ids");
+            return -1;
+        }
+        /* should be unreachable */
+        return -1;
+    }
+#endif
     return_value = !result;
 #endif
 
@@ -3002,6 +3154,7 @@ os_chmod_impl(PyObject *module, path_t *path, int mode, int dir_fd,
 
 #ifdef HAVE_FCHMODAT
     int fchmodat_nofollow_unsupported = 0;
+    int fchmodat_unsupported = 0;
 #endif
 
 #if !(defined(HAVE_FCHMODAT) || defined(HAVE_LCHMOD))
@@ -3037,42 +3190,56 @@ os_chmod_impl(PyObject *module, path_t *path, int mode, int dir_fd,
     if (path->fd != -1)
         result = fchmod(path->fd, mode);
     else
-#endif
+#endif /* HAVE_CHMOD */
 #ifdef HAVE_LCHMOD
     if ((!follow_symlinks) && (dir_fd == DEFAULT_DIR_FD))
         result = lchmod(path->narrow, mode);
     else
-#endif
+#endif /* HAVE_LCHMOD */
 #ifdef HAVE_FCHMODAT
     if ((dir_fd != DEFAULT_DIR_FD) || !follow_symlinks) {
-        /*
-         * fchmodat() doesn't currently support AT_SYMLINK_NOFOLLOW!
-         * The documentation specifically shows how to use it,
-         * and then says it isn't implemented yet.
-         * (true on linux with glibc 2.15, and openindiana 3.x)
-         *
-         * Once it is supported, os.chmod will automatically
-         * support dir_fd and follow_symlinks=False.  (Hopefully.)
-         * Until then, we need to be careful what exception we raise.
-         */
-        result = fchmodat(dir_fd, path->narrow, mode,
-                          follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW);
-        /*
-         * But wait!  We can't throw the exception without allowing threads,
-         * and we can't do that in this nested scope.  (Macro trickery, sigh.)
-         */
-        fchmodat_nofollow_unsupported =
-                         result &&
-                         ((errno == ENOTSUP) || (errno == EOPNOTSUPP)) &&
-                         !follow_symlinks;
+        if (HAVE_FCHMODAT_RUNTIME) {
+            /*
+             * fchmodat() doesn't currently support AT_SYMLINK_NOFOLLOW!
+             * The documentation specifically shows how to use it,
+             * and then says it isn't implemented yet.
+             * (true on linux with glibc 2.15, and openindiana 3.x)
+             *
+             * Once it is supported, os.chmod will automatically
+             * support dir_fd and follow_symlinks=False.  (Hopefully.)
+             * Until then, we need to be careful what exception we raise.
+             */
+            result = fchmodat(dir_fd, path->narrow, mode,
+                              follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW);
+            /*
+             * But wait!  We can't throw the exception without allowing threads,
+             * and we can't do that in this nested scope.  (Macro trickery, sigh.)
+             */
+            fchmodat_nofollow_unsupported =
+                             result &&
+                             ((errno == ENOTSUP) || (errno == EOPNOTSUPP)) &&
+                             !follow_symlinks;
+        } else {
+            fchmodat_unsupported = 1;
+            fchmodat_nofollow_unsupported = 1;
+
+            result = -1;
+        }
     }
     else
-#endif
+#endif /* HAVE_FHCMODAT */
         result = chmod(path->narrow, mode);
     Py_END_ALLOW_THREADS
 
     if (result) {
 #ifdef HAVE_FCHMODAT
+        if (fchmodat_unsupported) {
+            if (dir_fd != DEFAULT_DIR_FD) {
+                argument_unavailable_error("chmod", "dir_fd");
+                return NULL;
+            }
+        }
+
         if (fchmodat_nofollow_unsupported) {
             if (dir_fd != DEFAULT_DIR_FD)
                 dir_fd_and_follow_symlinks_invalid("chmod",
@@ -3082,10 +3249,10 @@ os_chmod_impl(PyObject *module, path_t *path, int mode, int dir_fd,
             return NULL;
         }
         else
-#endif
+#endif /* HAVE_FCHMODAT */
         return path_error(path);
     }
-#endif
+#endif /* MS_WINDOWS */
 
     Py_RETURN_NONE;
 }
@@ -3373,6 +3540,10 @@ os_chown_impl(PyObject *module, path_t *path, uid_t uid, gid_t gid,
 {
     int result;
 
+#if defined(HAVE_FCHOWNAT)
+    int fchownat_unsupported = 0;
+#endif
+
 #if !(defined(HAVE_LCHOWN) || defined(HAVE_FCHOWNAT))
     if (follow_symlinks_specified("chown", follow_symlinks))
         return NULL;
@@ -3381,19 +3552,6 @@ os_chown_impl(PyObject *module, path_t *path, uid_t uid, gid_t gid,
         fd_and_follow_symlinks_invalid("chown", path->fd, follow_symlinks))
         return NULL;
 
-#ifdef __APPLE__
-    /*
-     * This is for Mac OS X 10.3, which doesn't have lchown.
-     * (But we still have an lchown symbol because of weak-linking.)
-     * It doesn't have fchownat either.  So there's no possibility
-     * of a graceful failover.
-     */
-    if ((!follow_symlinks) && (lchown == NULL)) {
-        follow_symlinks_specified("chown", follow_symlinks);
-        return NULL;
-    }
-#endif
-
     if (PySys_Audit("os.chown", "OIIi", path->object, uid, gid,
                     dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) {
         return NULL;
@@ -3411,14 +3569,28 @@ os_chown_impl(PyObject *module, path_t *path, uid_t uid, gid_t gid,
     else
 #endif
 #ifdef HAVE_FCHOWNAT
-    if ((dir_fd != DEFAULT_DIR_FD) || (!follow_symlinks))
+    if ((dir_fd != DEFAULT_DIR_FD) || (!follow_symlinks)) {
+      if (HAVE_FCHOWNAT_RUNTIME) {
         result = fchownat(dir_fd, path->narrow, uid, gid,
                           follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW);
-    else
+      } else {
+         fchownat_unsupported = 1;
+      }
+    } else
 #endif
         result = chown(path->narrow, uid, gid);
     Py_END_ALLOW_THREADS
 
+#ifdef HAVE_FCHOWNAT
+    if (fchownat_unsupported) {
+        /* This would be incorrect if the current platform
+         * doesn't support lchown.
+         */
+        argument_unavailable_error(NULL, "dir_fd");
+        return NULL;
+    }
+#endif
+
     if (result)
         return path_error(path);
 
@@ -3664,6 +3836,9 @@ os_link_impl(PyObject *module, path_t *src, path_t *dst, int src_dir_fd,
 #else
     int result;
 #endif
+#if defined(HAVE_LINKAT)
+    int linkat_unavailable = 0;
+#endif
 
 #ifndef HAVE_LINKAT
     if ((src_dir_fd != DEFAULT_DIR_FD) || (dst_dir_fd != DEFAULT_DIR_FD)) {
@@ -3698,15 +3873,43 @@ os_link_impl(PyObject *module, path_t *src, path_t *dst, int src_dir_fd,
 #ifdef HAVE_LINKAT
     if ((src_dir_fd != DEFAULT_DIR_FD) ||
         (dst_dir_fd != DEFAULT_DIR_FD) ||
-        (!follow_symlinks))
-        result = linkat(src_dir_fd, src->narrow,
-            dst_dir_fd, dst->narrow,
-            follow_symlinks ? AT_SYMLINK_FOLLOW : 0);
+        (!follow_symlinks)) {
+
+        if (HAVE_LINKAT_RUNTIME) {
+
+            result = linkat(src_dir_fd, src->narrow,
+                dst_dir_fd, dst->narrow,
+                follow_symlinks ? AT_SYMLINK_FOLLOW : 0);
+
+        }
+#ifdef __APPLE__
+        else {
+            if (src_dir_fd == DEFAULT_DIR_FD && dst_dir_fd == DEFAULT_DIR_FD) {
+                /* See issue 41355: This matches the behaviour of !HAVE_LINKAT */
+                result = link(src->narrow, dst->narrow);
+            } else {
+                linkat_unavailable = 1;
+            }
+        }
+#endif
+    }
     else
 #endif /* HAVE_LINKAT */
         result = link(src->narrow, dst->narrow);
     Py_END_ALLOW_THREADS
 
+#ifdef HAVE_LINKAT
+    if (linkat_unavailable) {
+        /* Either or both dir_fd arguments were specified */
+        if (src_dir_fd  != DEFAULT_DIR_FD) {
+            argument_unavailable_error("link", "src_dir_fd");
+        } else {
+            argument_unavailable_error("link", "dst_dir_fd");
+        }
+        return NULL;
+    }
+#endif
+
     if (result)
         return path_error2(src, dst);
 #endif /* MS_WINDOWS */
@@ -3829,6 +4032,7 @@ _posix_listdir(path_t *path, PyObject *list)
     errno = 0;
 #ifdef HAVE_FDOPENDIR
     if (path->fd != -1) {
+      if (HAVE_FDOPENDIR_RUNTIME) {
         /* closedir() closes the FD, so we duplicate it */
         fd = _Py_dup(path->fd);
         if (fd == -1)
@@ -3839,6 +4043,11 @@ _posix_listdir(path_t *path, PyObject *list)
         Py_BEGIN_ALLOW_THREADS
         dirp = fdopendir(fd);
         Py_END_ALLOW_THREADS
+      } else {
+        PyErr_SetString(PyExc_TypeError,
+            "listdir: path should be string, bytes, os.PathLike or None, not int");
+        return NULL;
+      }
     }
     else
 #endif
@@ -4152,6 +4361,9 @@ os_mkdir_impl(PyObject *module, path_t *path, int mode, int dir_fd)
 /*[clinic end generated code: output=a70446903abe821f input=e965f68377e9b1ce]*/
 {
     int result;
+#ifdef HAVE_MKDIRAT
+    int mkdirat_unavailable = 0;
+#endif
 
     if (PySys_Audit("os.mkdir", "Oii", path->object, mode,
                     dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) {
@@ -4168,9 +4380,14 @@ os_mkdir_impl(PyObject *module, path_t *path, int mode, int dir_fd)
 #else
     Py_BEGIN_ALLOW_THREADS
 #if HAVE_MKDIRAT
-    if (dir_fd != DEFAULT_DIR_FD)
+    if (dir_fd != DEFAULT_DIR_FD) {
+      if (HAVE_MKDIRAT_RUNTIME) {
         result = mkdirat(dir_fd, path->narrow, mode);
-    else
+
+      } else {
+        mkdirat_unavailable = 1;
+      }
+    } else
 #endif
 #if defined(__WATCOMC__) && !defined(__QNX__)
         result = mkdir(path->narrow);
@@ -4178,6 +4395,14 @@ os_mkdir_impl(PyObject *module, path_t *path, int mode, int dir_fd)
         result = mkdir(path->narrow, mode);
 #endif
     Py_END_ALLOW_THREADS
+
+#if HAVE_MKDIRAT
+    if (mkdirat_unavailable) {
+        argument_unavailable_error(NULL, "dir_fd");
+        return NULL;
+    }
+#endif
+
     if (result < 0)
         return path_error(path);
 #endif /* MS_WINDOWS */
@@ -4287,6 +4512,10 @@ internal_rename(path_t *src, path_t *dst, int src_dir_fd, int dst_dir_fd, int is
     const char *function_name = is_replace ? "replace" : "rename";
     int dir_fd_specified;
 
+#ifdef HAVE_RENAMEAT
+    int renameat_unavailable = 0;
+#endif
+
 #ifdef MS_WINDOWS
     BOOL result;
     int flags = is_replace ? MOVEFILE_REPLACE_EXISTING : 0;
@@ -4326,13 +4555,25 @@ internal_rename(path_t *src, path_t *dst, int src_dir_fd, int dst_dir_fd, int is
 
     Py_BEGIN_ALLOW_THREADS
 #ifdef HAVE_RENAMEAT
-    if (dir_fd_specified)
-        result = renameat(src_dir_fd, src->narrow, dst_dir_fd, dst->narrow);
-    else
+    if (dir_fd_specified) {
+        if (HAVE_RENAMEAT_RUNTIME) {
+            result = renameat(src_dir_fd, src->narrow, dst_dir_fd, dst->narrow);
+        } else {
+            renameat_unavailable = 1;
+        }
+    } else
 #endif
     result = rename(src->narrow, dst->narrow);
     Py_END_ALLOW_THREADS
 
+
+#ifdef HAVE_RENAMEAT
+    if (renameat_unavailable) {
+        argument_unavailable_error(function_name, "src_dir_fd and dst_dir_fd");
+        return NULL;
+    }
+#endif
+
     if (result)
         return path_error2(src, dst);
 #endif
@@ -4408,6 +4649,9 @@ os_rmdir_impl(PyObject *module, path_t *path, int dir_fd)
 /*[clinic end generated code: output=080eb54f506e8301 input=38c8b375ca34a7e2]*/
 {
     int result;
+#ifdef HAVE_UNLINKAT
+    int unlinkat_unavailable = 0;
+#endif
 
     if (PySys_Audit("os.rmdir", "Oi", path->object,
                     dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) {
@@ -4420,14 +4664,26 @@ os_rmdir_impl(PyObject *module, path_t *path, int dir_fd)
     result = !RemoveDirectoryW(path->wide);
 #else
 #ifdef HAVE_UNLINKAT
-    if (dir_fd != DEFAULT_DIR_FD)
+    if (dir_fd != DEFAULT_DIR_FD) {
+      if (HAVE_UNLINKAT_RUNTIME) {
         result = unlinkat(dir_fd, path->narrow, AT_REMOVEDIR);
-    else
+      } else {
+        unlinkat_unavailable = 1;
+        result = -1;
+      }
+    } else
 #endif
         result = rmdir(path->narrow);
 #endif
     Py_END_ALLOW_THREADS
 
+#ifdef HAVE_UNLINKAT
+    if (unlinkat_unavailable) {
+        argument_unavailable_error("rmdir", "dir_fd");
+        return NULL;
+    }
+#endif
+
     if (result)
         return path_error(path);
 
@@ -4571,6 +4827,9 @@ os_unlink_impl(PyObject *module, path_t *path, int dir_fd)
 /*[clinic end generated code: output=621797807b9963b1 input=d7bcde2b1b2a2552]*/
 {
     int result;
+#ifdef HAVE_UNLINKAT
+    int unlinkat_unavailable = 0;
+#endif
 
     if (PySys_Audit("os.remove", "Oi", path->object,
                     dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) {
@@ -4584,15 +4843,27 @@ os_unlink_impl(PyObject *module, path_t *path, int dir_fd)
     result = !Py_DeleteFileW(path->wide);
 #else
 #ifdef HAVE_UNLINKAT
-    if (dir_fd != DEFAULT_DIR_FD)
+    if (dir_fd != DEFAULT_DIR_FD) {
+      if (HAVE_UNLINKAT_RUNTIME) {
+
         result = unlinkat(dir_fd, path->narrow, 0);
-    else
+      } else {
+        unlinkat_unavailable = 1;
+      }
+    } else
 #endif /* HAVE_UNLINKAT */
         result = unlink(path->narrow);
 #endif
     _Py_END_SUPPRESS_IPH
     Py_END_ALLOW_THREADS
 
+#ifdef HAVE_UNLINKAT
+    if (unlinkat_unavailable) {
+        argument_unavailable_error(NULL, "dir_fd");
+        return NULL;
+    }
+#endif
+
     if (result)
         return path_error(path);
 
@@ -4763,7 +5034,16 @@ typedef struct {
 static int
 utime_dir_fd(utime_t *ut, int dir_fd, const char *path, int follow_symlinks)
 {
-#ifdef HAVE_UTIMENSAT
+#if defined(__APPLE__) &&  defined(HAVE_UTIMENSAT)
+    if (HAVE_UTIMENSAT_RUNTIME) {
+        int flags = follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW;
+        UTIME_TO_TIMESPEC;
+        return utimensat(dir_fd, path, time, flags);
+    }  else {
+        errno = ENOSYS;
+        return -1;
+    }
+#elif defined(HAVE_UTIMENSAT)
     int flags = follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW;
     UTIME_TO_TIMESPEC;
     return utimensat(dir_fd, path, time, flags);
@@ -4790,11 +5070,30 @@ static int
 utime_fd(utime_t *ut, int fd)
 {
 #ifdef HAVE_FUTIMENS
+
+    if (HAVE_FUTIMENS_RUNTIME) {
+
     UTIME_TO_TIMESPEC;
     return futimens(fd, time);
-#else
+
+    } else
+#ifndef HAVE_FUTIMES
+    {
+        /* Not sure if this can happen */
+        PyErr_SetString(
+            PyExc_RuntimeError,
+            "neither futimens nor futimes are supported"
+            " on this system");
+        return -1;
+    }
+#endif
+
+#endif
+#ifdef HAVE_FUTIMES
+    {
     UTIME_TO_TIMEVAL;
     return futimes(fd, time);
+    }
 #endif
 }
 
@@ -4813,11 +5112,27 @@ static int
 utime_nofollow_symlinks(utime_t *ut, const char *path)
 {
 #ifdef HAVE_UTIMENSAT
-    UTIME_TO_TIMESPEC;
-    return utimensat(DEFAULT_DIR_FD, path, time, AT_SYMLINK_NOFOLLOW);
-#else
+    if (HAVE_UTIMENSAT_RUNTIME) {
+        UTIME_TO_TIMESPEC;
+        return utimensat(DEFAULT_DIR_FD, path, time, AT_SYMLINK_NOFOLLOW);
+    } else
+#ifndef HAVE_LUTIMES
+    {
+        /* Not sure if this can happen */
+        PyErr_SetString(
+            PyExc_RuntimeError,
+            "neither utimensat nor lutimes are supported"
+            " on this system");
+        return -1;
+    }
+#endif
+#endif
+
+#ifdef HAVE_LUTIMES
+    {
     UTIME_TO_TIMEVAL;
     return lutimes(path, time);
+    }
 #endif
 }
 
@@ -4828,7 +5143,15 @@ utime_nofollow_symlinks(utime_t *ut, const char *path)
 static int
 utime_default(utime_t *ut, const char *path)
 {
-#ifdef HAVE_UTIMENSAT
+#if defined(__APPLE__) && defined(HAVE_UTIMENSAT)
+    if (HAVE_UTIMENSAT_RUNTIME) {
+        UTIME_TO_TIMESPEC;
+        return utimensat(DEFAULT_DIR_FD, path, time, 0);
+    } else {
+        UTIME_TO_TIMEVAL;
+        return utimes(path, time);
+    }
+#elif defined(HAVE_UTIMENSAT)
     UTIME_TO_TIMESPEC;
     return utimensat(DEFAULT_DIR_FD, path, time, 0);
 #elif defined(HAVE_UTIMES)
@@ -5037,9 +5360,10 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns,
 #endif
 
 #if defined(HAVE_FUTIMESAT) || defined(HAVE_UTIMENSAT)
-    if ((dir_fd != DEFAULT_DIR_FD) || (!follow_symlinks))
+    if ((dir_fd != DEFAULT_DIR_FD) || (!follow_symlinks)) {
         result = utime_dir_fd(&utime, dir_fd, path->narrow, follow_symlinks);
-    else
+
+    } else
 #endif
 
 #if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMENS)
@@ -5052,6 +5376,14 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns,
 
     Py_END_ALLOW_THREADS
 
+#if defined(__APPLE__) && defined(HAVE_UTIMENSAT)
+    /* See utime_dir_fd implementation */
+    if (result == -1 && errno == ENOSYS) {
+        argument_unavailable_error(NULL, "dir_fd");
+        return NULL;
+    }
+#endif
+
     if (result < 0) {
         /* see previous comment about not putting filename in error here */
         posix_error();
@@ -5450,6 +5782,9 @@ parse_posix_spawn_flags(PyObject *module, const char *func_name, PyObject *setpg
     }
 
     if (setsid) {
+#ifdef HAVE_POSIX_SPAWN_SETSID_RUNTIME
+        if (HAVE_POSIX_SPAWN_SETSID_RUNTIME) {
+#endif
 #ifdef POSIX_SPAWN_SETSID
         all_flags |= POSIX_SPAWN_SETSID;
 #elif defined(POSIX_SPAWN_SETSID_NP)
@@ -5458,6 +5793,14 @@ parse_posix_spawn_flags(PyObject *module, const char *func_name, PyObject *setpg
         argument_unavailable_error(func_name, "setsid");
         return -1;
 #endif
+
+#ifdef HAVE_POSIX_SPAWN_SETSID_RUNTIME
+        } else {
+            argument_unavailable_error(func_name, "setsid");
+            return -1;
+        }
+#endif /* HAVE_POSIX_SPAWN_SETSID_RUNTIME */
+
     }
 
    if (setsigmask) {
@@ -8068,16 +8411,30 @@ os_readlink_impl(PyObject *module, path_t *path, int dir_fd)
 #if defined(HAVE_READLINK)
     char buffer[MAXPATHLEN+1];
     ssize_t length;
+#ifdef HAVE_READLINKAT
+    int readlinkat_unavailable = 0;
+#endif
 
     Py_BEGIN_ALLOW_THREADS
 #ifdef HAVE_READLINKAT
-    if (dir_fd != DEFAULT_DIR_FD)
-        length = readlinkat(dir_fd, path->narrow, buffer, MAXPATHLEN);
-    else
+    if (dir_fd != DEFAULT_DIR_FD) {
+        if (HAVE_READLINKAT_RUNTIME) {
+            length = readlinkat(dir_fd, path->narrow, buffer, MAXPATHLEN);
+        } else {
+            readlinkat_unavailable = 1;
+        }
+    } else
 #endif
         length = readlink(path->narrow, buffer, MAXPATHLEN);
     Py_END_ALLOW_THREADS
 
+#ifdef HAVE_READLINKAT
+    if (readlinkat_unavailable) {
+        argument_unavailable_error(NULL, "dir_fd");
+        return NULL;
+    }
+#endif
+
     if (length < 0) {
         return path_error(path);
     }
@@ -8273,6 +8630,9 @@ os_symlink_impl(PyObject *module, path_t *src, path_t *dst,
     static int windows_has_symlink_unprivileged_flag = TRUE;
 #else
     int result;
+#ifdef HAVE_SYMLINKAT
+    int symlinkat_unavailable = 0;
+#endif
 #endif
 
     if (PySys_Audit("os.symlink", "OOi", src->object, dst->object,
@@ -8335,14 +8695,25 @@ os_symlink_impl(PyObject *module, path_t *src, path_t *dst,
     }
 
     Py_BEGIN_ALLOW_THREADS
-#if HAVE_SYMLINKAT
-    if (dir_fd != DEFAULT_DIR_FD)
-        result = symlinkat(src->narrow, dir_fd, dst->narrow);
-    else
+#ifdef HAVE_SYMLINKAT
+    if (dir_fd != DEFAULT_DIR_FD) {
+        if (HAVE_SYMLINKAT_RUNTIME) {
+            result = symlinkat(src->narrow, dir_fd, dst->narrow);
+        } else {
+            symlinkat_unavailable = 1;
+        }
+    } else
 #endif
         result = symlink(src->narrow, dst->narrow);
     Py_END_ALLOW_THREADS
 
+#ifdef HAVE_SYMLINKAT
+    if (symlinkat_unavailable) {
+          argument_unavailable_error(NULL, "dir_fd");
+          return NULL;
+    }
+#endif
+
     if (result)
         return path_error2(src, dst);
 #endif
@@ -8613,6 +8984,9 @@ os_open_impl(PyObject *module, path_t *path, int flags, int mode, int dir_fd)
 {
     int fd;
     int async_err = 0;
+#ifdef HAVE_OPENAT
+    int openat_unavailable = 0;
+#endif
 
 #ifdef O_CLOEXEC
     int *atomic_flag_works = &_Py_open_cloexec_works;
@@ -8637,9 +9011,15 @@ os_open_impl(PyObject *module, path_t *path, int flags, int mode, int dir_fd)
         fd = _wopen(path->wide, flags, mode);
 #else
 #ifdef HAVE_OPENAT
-        if (dir_fd != DEFAULT_DIR_FD)
-            fd = openat(dir_fd, path->narrow, flags, mode);
-        else
+        if (dir_fd != DEFAULT_DIR_FD) {
+            if (HAVE_OPENAT_RUNTIME) {
+                fd = openat(dir_fd, path->narrow, flags, mode);
+
+            } else {
+                openat_unavailable = 1;
+                fd = -1;
+            }
+        } else
 #endif /* HAVE_OPENAT */
             fd = open(path->narrow, flags, mode);
 #endif /* !MS_WINDOWS */
@@ -8647,6 +9027,13 @@ os_open_impl(PyObject *module, path_t *path, int flags, int mode, int dir_fd)
     } while (fd < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
     _Py_END_SUPPRESS_IPH
 
+#ifdef HAVE_OPENAT
+    if (openat_unavailable) {
+        argument_unavailable_error(NULL, "dir_fd");
+        return -1;
+    }
+#endif
+
     if (fd < 0) {
         if (!async_err)
             PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path->object);
@@ -9229,12 +9616,25 @@ os_preadv_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset,
     } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
 #else
     do {
+#ifdef __APPLE__
+/* This entire function will be removed from the module dict when the API
+ * is not available.
+ */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability"
+#pragma clang diagnostic ignored "-Wunguarded-availability-new"
+#endif
         Py_BEGIN_ALLOW_THREADS
         _Py_BEGIN_SUPPRESS_IPH
         n = preadv(fd, iov, cnt, offset);
         _Py_END_SUPPRESS_IPH
         Py_END_ALLOW_THREADS
     } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
+
+#ifdef __APPLE__
+#pragma clang diagnostic pop
+#endif
+
 #endif
 
     iov_cleanup(iov, buf, cnt);
@@ -9469,11 +9869,26 @@ done:
     if (offset >= st.st_size) {
         return Py_BuildValue("i", 0);
     }
+
+    // On illumos specifically sendfile() may perform a partial write but
+    // return -1/an error (in one confirmed case the destination socket
+    // had a 5 second timeout set and errno was EAGAIN) and it's on the client
+    // code to check if the offset parameter was modified by sendfile().
+    //
+    // We need this variable to track said change.
+    off_t original_offset = offset;
 #endif
 
     do {
         Py_BEGIN_ALLOW_THREADS
         ret = sendfile(out_fd, in_fd, &offset, count);
+#if defined(__sun) && defined(__SVR4)
+        // This handles illumos-specific sendfile() partial write behavior,
+        // see a comment above for more details.
+        if (ret < 0 && offset != original_offset) {
+            ret = offset - original_offset;
+        }
+#endif
         Py_END_ALLOW_THREADS
     } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
     if (ret < 0)
@@ -9841,6 +10256,15 @@ os_pwritev_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset,
         Py_END_ALLOW_THREADS
     } while (result < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
 #else
+
+#ifdef __APPLE__
+/* This entire function will be removed from the module dict when the API
+ * is not available.
+ */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability"
+#pragma clang diagnostic ignored "-Wunguarded-availability-new"
+#endif
     do {
         Py_BEGIN_ALLOW_THREADS
         _Py_BEGIN_SUPPRESS_IPH
@@ -9848,6 +10272,11 @@ os_pwritev_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset,
         _Py_END_SUPPRESS_IPH
         Py_END_ALLOW_THREADS
     } while (result < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
+
+#ifdef __APPLE__
+#pragma clang diagnostic pop
+#endif
+
 #endif
 
     iov_cleanup(iov, buf, cnt);
@@ -10737,13 +11166,6 @@ os_statvfs_impl(PyObject *module, path_t *path)
     Py_BEGIN_ALLOW_THREADS
 #ifdef HAVE_FSTATVFS
     if (path->fd != -1) {
-#ifdef __APPLE__
-        /* handle weak-linking on Mac OS X 10.3 */
-        if (fstatvfs == NULL) {
-            fd_specified("statvfs", path->fd);
-            return NULL;
-        }
-#endif
         result = fstatvfs(path->fd, &st);
     }
     else
@@ -12817,12 +13239,17 @@ DirEntry_fetch_stat(PyObject *module, DirEntry *self, int follow_symlinks)
     const char *path = PyBytes_AS_STRING(ub);
     if (self->dir_fd != DEFAULT_DIR_FD) {
 #ifdef HAVE_FSTATAT
+      if (HAVE_FSTATAT_RUNTIME) {
         result = fstatat(self->dir_fd, path, &st,
                          follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW);
-#else
+      } else
+
+#endif /* HAVE_FSTATAT */
+      {
+        Py_DECREF(ub);
         PyErr_SetString(PyExc_NotImplementedError, "can't fetch stat");
         return NULL;
-#endif /* HAVE_FSTATAT */
+      }
     }
     else
 #endif
@@ -13608,7 +14035,8 @@ os_scandir_impl(PyObject *module, path_t *path)
 #else /* POSIX */
     errno = 0;
 #ifdef HAVE_FDOPENDIR
-    if (path->fd != -1) {
+    if (iterator->path.fd != -1) {
+      if (HAVE_FDOPENDIR_RUNTIME) {
         /* closedir() closes the FD, so we duplicate it */
         fd = _Py_dup(path->fd);
         if (fd == -1)
@@ -13617,6 +14045,11 @@ os_scandir_impl(PyObject *module, path_t *path)
         Py_BEGIN_ALLOW_THREADS
         iterator->dirp = fdopendir(fd);
         Py_END_ALLOW_THREADS
+      } else {
+        PyErr_SetString(PyExc_TypeError,
+            "scandir: path should be string, bytes, os.PathLike or None, not int");
+        return NULL;
+      }
     }
     else
 #endif
@@ -14687,137 +15120,210 @@ all_ins(PyObject *m)
 }
 
 
-static const char * const have_functions[] = {
+
+#define PROBE(name, test) \
+   static int name(void)  \
+   {                      \
+      if (test) {        \
+          return 1;       \
+      } else {            \
+          return 0;       \
+      }                   \
+   }
+
+#ifdef HAVE_FSTATAT
+PROBE(probe_fstatat, HAVE_FSTATAT_RUNTIME)
+#endif
 
 #ifdef HAVE_FACCESSAT
-    "HAVE_FACCESSAT",
+PROBE(probe_faccessat, HAVE_FACCESSAT_RUNTIME)
+#endif
+
+#ifdef HAVE_FCHMODAT
+PROBE(probe_fchmodat, HAVE_FCHMODAT_RUNTIME)
+#endif
+
+#ifdef HAVE_FCHOWNAT
+PROBE(probe_fchownat, HAVE_FCHOWNAT_RUNTIME)
+#endif
+
+#ifdef HAVE_LINKAT
+PROBE(probe_linkat, HAVE_LINKAT_RUNTIME)
+#endif
+
+#ifdef HAVE_FDOPENDIR
+PROBE(probe_fdopendir, HAVE_FDOPENDIR_RUNTIME)
+#endif
+
+#ifdef HAVE_MKDIRAT
+PROBE(probe_mkdirat, HAVE_MKDIRAT_RUNTIME)
+#endif
+
+#ifdef HAVE_RENAMEAT
+PROBE(probe_renameat, HAVE_RENAMEAT_RUNTIME)
+#endif
+
+#ifdef HAVE_UNLINKAT
+PROBE(probe_unlinkat, HAVE_UNLINKAT_RUNTIME)
+#endif
+
+#ifdef HAVE_OPENAT
+PROBE(probe_openat, HAVE_OPENAT_RUNTIME)
+#endif
+
+#ifdef HAVE_READLINKAT
+PROBE(probe_readlinkat, HAVE_READLINKAT_RUNTIME)
+#endif
+
+#ifdef HAVE_SYMLINKAT
+PROBE(probe_symlinkat, HAVE_SYMLINKAT_RUNTIME)
+#endif
+
+#ifdef HAVE_FUTIMENS
+PROBE(probe_futimens, HAVE_FUTIMENS_RUNTIME)
+#endif
+
+#ifdef HAVE_UTIMENSAT
+PROBE(probe_utimensat, HAVE_UTIMENSAT_RUNTIME)
+#endif
+
+
+
+
+static const struct have_function {
+    const char * const label;
+    int (*probe)(void);
+} have_functions[] = {
+
+#ifdef HAVE_FACCESSAT
+    { "HAVE_FACCESSAT", probe_faccessat },
 #endif
 
 #ifdef HAVE_FCHDIR
-    "HAVE_FCHDIR",
+    { "HAVE_FCHDIR", NULL },
 #endif
 
 #ifdef HAVE_FCHMOD
-    "HAVE_FCHMOD",
+    { "HAVE_FCHMOD", NULL },
 #endif
 
 #ifdef HAVE_FCHMODAT
-    "HAVE_FCHMODAT",
+    { "HAVE_FCHMODAT", probe_fchmodat },
 #endif
 
 #ifdef HAVE_FCHOWN
-    "HAVE_FCHOWN",
+    { "HAVE_FCHOWN", NULL },
 #endif
 
 #ifdef HAVE_FCHOWNAT
-    "HAVE_FCHOWNAT",
+    { "HAVE_FCHOWNAT", probe_fchownat },
 #endif
 
 #ifdef HAVE_FEXECVE
-    "HAVE_FEXECVE",
+    { "HAVE_FEXECVE", NULL },
 #endif
 
 #ifdef HAVE_FDOPENDIR
-    "HAVE_FDOPENDIR",
+    { "HAVE_FDOPENDIR", probe_fdopendir },
 #endif
 
 #ifdef HAVE_FPATHCONF
-    "HAVE_FPATHCONF",
+    { "HAVE_FPATHCONF", NULL },
 #endif
 
 #ifdef HAVE_FSTATAT
-    "HAVE_FSTATAT",
+    { "HAVE_FSTATAT", probe_fstatat },
 #endif
 
 #ifdef HAVE_FSTATVFS
-    "HAVE_FSTATVFS",
+    { "HAVE_FSTATVFS", NULL },
 #endif
 
 #if defined HAVE_FTRUNCATE || defined MS_WINDOWS
-    "HAVE_FTRUNCATE",
+    { "HAVE_FTRUNCATE", NULL },
 #endif
 
 #ifdef HAVE_FUTIMENS
-    "HAVE_FUTIMENS",
+    { "HAVE_FUTIMENS", probe_futimens },
 #endif
 
 #ifdef HAVE_FUTIMES
-    "HAVE_FUTIMES",
+    { "HAVE_FUTIMES", NULL },
 #endif
 
 #ifdef HAVE_FUTIMESAT
-    "HAVE_FUTIMESAT",
+    { "HAVE_FUTIMESAT", NULL },
 #endif
 
 #ifdef HAVE_LINKAT
-    "HAVE_LINKAT",
+    { "HAVE_LINKAT", probe_linkat },
 #endif
 
 #ifdef HAVE_LCHFLAGS
-    "HAVE_LCHFLAGS",
+    { "HAVE_LCHFLAGS", NULL },
 #endif
 
 #ifdef HAVE_LCHMOD
-    "HAVE_LCHMOD",
+    { "HAVE_LCHMOD", NULL },
 #endif
 
 #ifdef HAVE_LCHOWN
-    "HAVE_LCHOWN",
+    { "HAVE_LCHOWN", NULL },
 #endif
 
 #ifdef HAVE_LSTAT
-    "HAVE_LSTAT",
+    { "HAVE_LSTAT", NULL },
 #endif
 
 #ifdef HAVE_LUTIMES
-    "HAVE_LUTIMES",
+    { "HAVE_LUTIMES", NULL },
 #endif
 
 #ifdef HAVE_MEMFD_CREATE
-    "HAVE_MEMFD_CREATE",
+    { "HAVE_MEMFD_CREATE", NULL },
 #endif
 
 #ifdef HAVE_MKDIRAT
-    "HAVE_MKDIRAT",
+    { "HAVE_MKDIRAT", probe_mkdirat },
 #endif
 
 #ifdef HAVE_MKFIFOAT
-    "HAVE_MKFIFOAT",
+    { "HAVE_MKFIFOAT", NULL },
 #endif
 
 #ifdef HAVE_MKNODAT
-    "HAVE_MKNODAT",
+    { "HAVE_MKNODAT", NULL },
 #endif
 
 #ifdef HAVE_OPENAT
-    "HAVE_OPENAT",
+    { "HAVE_OPENAT", probe_openat },
 #endif
 
 #ifdef HAVE_READLINKAT
-    "HAVE_READLINKAT",
+    { "HAVE_READLINKAT", probe_readlinkat },
 #endif
 
 #ifdef HAVE_RENAMEAT
-    "HAVE_RENAMEAT",
+    { "HAVE_RENAMEAT", probe_renameat },
 #endif
 
 #ifdef HAVE_SYMLINKAT
-    "HAVE_SYMLINKAT",
+    { "HAVE_SYMLINKAT", probe_symlinkat },
 #endif
 
 #ifdef HAVE_UNLINKAT
-    "HAVE_UNLINKAT",
+    { "HAVE_UNLINKAT", probe_unlinkat },
 #endif
 
 #ifdef HAVE_UTIMENSAT
-    "HAVE_UTIMENSAT",
+    { "HAVE_UTIMENSAT", probe_utimensat },
 #endif
 
 #ifdef MS_WINDOWS
-    "MS_WINDOWS",
+    { "MS_WINDOWS", NULL },
 #endif
 
-    NULL
+    { NULL, NULL }
 };
 
 
@@ -14826,6 +15332,23 @@ posixmodule_exec(PyObject *m)
 {
     _posixstate *state = get_posix_state(m);
 
+#if defined(HAVE_PWRITEV)
+    if (HAVE_PWRITEV_RUNTIME) {} else {
+        PyObject* dct = PyModule_GetDict(m);
+
+        if (dct == NULL) {
+            return -1;
+        }
+
+        if (PyDict_DelItemString(dct, "pwritev") == -1) {
+            PyErr_Clear();
+        }
+        if (PyDict_DelItemString(dct, "preadv") == -1) {
+            PyErr_Clear();
+        }
+    }
+#endif
+
     /* Initialize environ dictionary */
     PyObject *v = convertenviron();
     Py_XINCREF(v);
@@ -14938,44 +15461,6 @@ posixmodule_exec(PyObject *m)
     PyModule_AddObject(m, "uname_result", (PyObject *)UnameResultType);
     state->UnameResultType = (PyObject *)UnameResultType;
 
-#ifdef __APPLE__
-    /*
-     * Step 2 of weak-linking support on Mac OS X.
-     *
-     * The code below removes functions that are not available on the
-     * currently active platform.
-     *
-     * This block allow one to use a python binary that was build on
-     * OSX 10.4 on OSX 10.3, without losing access to new APIs on
-     * OSX 10.4.
-     */
-#ifdef HAVE_FSTATVFS
-    if (fstatvfs == NULL) {
-        if (PyObject_DelAttrString(m, "fstatvfs") == -1) {
-            return -1;
-        }
-    }
-#endif /* HAVE_FSTATVFS */
-
-#ifdef HAVE_STATVFS
-    if (statvfs == NULL) {
-        if (PyObject_DelAttrString(m, "statvfs") == -1) {
-            return -1;
-        }
-    }
-#endif /* HAVE_STATVFS */
-
-# ifdef HAVE_LCHOWN
-    if (lchown == NULL) {
-        if (PyObject_DelAttrString(m, "lchown") == -1) {
-            return -1;
-        }
-    }
-#endif /* HAVE_LCHOWN */
-
-
-#endif /* __APPLE__ */
-
     if ((state->billion = PyLong_FromLong(1000000000)) == NULL)
         return -1;
 #if defined(HAVE_WAIT3) || defined(HAVE_WAIT4)
@@ -15005,14 +15490,17 @@ posixmodule_exec(PyObject *m)
     if (!list) {
         return -1;
     }
-    for (const char * const *trace = have_functions; *trace; trace++) {
-        PyObject *unicode = PyUnicode_DecodeASCII(*trace, strlen(*trace), NULL);
+    for (const struct have_function *trace = have_functions; trace->label; trace++) {
+        PyObject *unicode;
+        if (trace->probe && !trace->probe()) continue;
+        unicode = PyUnicode_DecodeASCII(trace->label, strlen(trace->label), NULL);
         if (!unicode)
             return -1;
         if (PyList_Append(list, unicode))
             return -1;
         Py_DECREF(unicode);
     }
+
     PyModule_AddObject(m, "_have_functions", list);
 
     return 0;
index 9041848..540e2d9 100644 (file)
@@ -1632,11 +1632,6 @@ PyInit__signal(void)
          goto finally;
 #endif
 
-#ifdef MS_WINDOWS
-    /* Create manual-reset event, initially unset */
-    sigint_event = CreateEvent(NULL, TRUE, FALSE, FALSE);
-#endif
-
     if (PyErr_Occurred()) {
         Py_DECREF(m);
         m = NULL;
@@ -1773,6 +1768,53 @@ PyOS_InitInterrupts(void)
     }
 }
 
+
+static int
+signal_install_handlers(void)
+{
+#ifdef SIGPIPE
+    PyOS_setsig(SIGPIPE, SIG_IGN);
+#endif
+#ifdef SIGXFZ
+    PyOS_setsig(SIGXFZ, SIG_IGN);
+#endif
+#ifdef SIGXFSZ
+    PyOS_setsig(SIGXFSZ, SIG_IGN);
+#endif
+
+    // Import _signal to install the Python SIGINT handler
+    PyObject *module = PyImport_ImportModule("_signal");
+    if (!module) {
+        return -1;
+    }
+    Py_DECREF(module);
+
+    return 0;
+}
+
+
+int
+_PySignal_Init(int install_signal_handlers)
+{
+#ifdef MS_WINDOWS
+    /* Create manual-reset event, initially unset */
+    sigint_event = CreateEvent(NULL, TRUE, FALSE, FALSE);
+    if (sigint_event == NULL) {
+        PyErr_SetFromWindowsErr(0);
+        return -1;
+    }
+#endif
+
+    if (install_signal_handlers) {
+        if (signal_install_handlers() < 0) {
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+
 void
 PyOS_FiniInterrupts(void)
 {
index 8a4d149..80eab30 100644 (file)
 #define _Py_tzname tzname
 #endif
 
+#if defined(__APPLE__ ) && defined(__has_builtin) 
+#  if __has_builtin(__builtin_available)
+#    define HAVE_CLOCK_GETTIME_RUNTIME __builtin_available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
+#  endif
+#endif
+#ifndef HAVE_CLOCK_GETTIME_RUNTIME
+#  define HAVE_CLOCK_GETTIME_RUNTIME 1
+#endif
+
 #define SEC_TO_NS (1000 * 1000 * 1000)
 
 /* Forward declarations */
@@ -149,6 +158,16 @@ perf_counter(_Py_clock_info_t *info)
 }
 
 #ifdef HAVE_CLOCK_GETTIME
+
+#ifdef __APPLE__
+/* 
+ * The clock_* functions will be removed from the module
+ * dict entirely when the C API is not available.
+ */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability"
+#endif
+
 static PyObject *
 time_clock_gettime(PyObject *self, PyObject *args)
 {
@@ -297,6 +316,11 @@ PyDoc_STRVAR(clock_getres_doc,
 "clock_getres(clk_id) -> floating point number\n\
 \n\
 Return the resolution (precision) of the specified clock clk_id.");
+
+#ifdef __APPLE__
+#pragma clang diagnostic pop
+#endif
+
 #endif   /* HAVE_CLOCK_GETRES */
 
 #ifdef HAVE_PTHREAD_GETCPUCLOCKID
@@ -1162,31 +1186,35 @@ _PyTime_GetProcessTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
 #if defined(HAVE_CLOCK_GETTIME) \
     && (defined(CLOCK_PROCESS_CPUTIME_ID) || defined(CLOCK_PROF))
     struct timespec ts;
+
+    if (HAVE_CLOCK_GETTIME_RUNTIME) {
+
 #ifdef CLOCK_PROF
-    const clockid_t clk_id = CLOCK_PROF;
-    const char *function = "clock_gettime(CLOCK_PROF)";
+        const clockid_t clk_id = CLOCK_PROF;
+        const char *function = "clock_gettime(CLOCK_PROF)";
 #else
-    const clockid_t clk_id = CLOCK_PROCESS_CPUTIME_ID;
-    const char *function = "clock_gettime(CLOCK_PROCESS_CPUTIME_ID)";
+        const clockid_t clk_id = CLOCK_PROCESS_CPUTIME_ID;
+        const char *function = "clock_gettime(CLOCK_PROCESS_CPUTIME_ID)";
 #endif
 
-    if (clock_gettime(clk_id, &ts) == 0) {
-        if (info) {
-            struct timespec res;
-            info->implementation = function;
-            info->monotonic = 1;
-            info->adjustable = 0;
-            if (clock_getres(clk_id, &res)) {
-                PyErr_SetFromErrno(PyExc_OSError);
-                return -1;
+        if (clock_gettime(clk_id, &ts) == 0) {
+            if (info) {
+                struct timespec res;
+                info->implementation = function;
+                info->monotonic = 1;
+                info->adjustable = 0;
+                if (clock_getres(clk_id, &res)) {
+                    PyErr_SetFromErrno(PyExc_OSError);
+                    return -1;
+                }
+                info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
             }
-            info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
-        }
 
-        if (_PyTime_FromTimespec(tp, &ts) < 0) {
-            return -1;
+            if (_PyTime_FromTimespec(tp, &ts) < 0) {
+                return -1;
+            }
+            return 0;
         }
-        return 0;
     }
 #endif
 
@@ -1371,8 +1399,35 @@ _PyTime_GetThreadTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
     return 0;
 }
 
+#elif defined(__sun) && defined(__SVR4)
+#define HAVE_THREAD_TIME
+static int
+_PyTime_GetThreadTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
+{
+    /* bpo-35455: On Solaris, CLOCK_THREAD_CPUTIME_ID clock is not always
+       available; use gethrvtime() to substitute this functionality. */
+    if (info) {
+        info->implementation = "gethrvtime()";
+        info->resolution = 1e-9;
+        info->monotonic = 1;
+        info->adjustable = 0;
+    }
+    *tp = _PyTime_FromNanoseconds(gethrvtime());
+    return 0;
+}
+
 #elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID)
 #define HAVE_THREAD_TIME
+
+#if defined(__APPLE__) && defined(__has_attribute) && __has_attribute(availability)
+static int
+_PyTime_GetThreadTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) 
+     __attribute__((availability(macos, introduced=10.12)))
+     __attribute__((availability(ios, introduced=10.0)))
+     __attribute__((availability(tvos, introduced=10.0)))
+     __attribute__((availability(watchos, introduced=3.0)));
+#endif
+
 static int
 _PyTime_GetThreadTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
 {
@@ -1404,6 +1459,15 @@ _PyTime_GetThreadTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
 #endif
 
 #ifdef HAVE_THREAD_TIME
+#ifdef __APPLE__
+/* 
+ * The clock_* functions will be removed from the module
+ * dict entirely when the C API is not available.
+ */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability"
+#endif
+
 static PyObject *
 time_thread_time(PyObject *self, PyObject *unused)
 {
@@ -1434,6 +1498,11 @@ PyDoc_STRVAR(thread_time_ns_doc,
 \n\
 Thread time for profiling as nanoseconds:\n\
 sum of the kernel and user-space CPU time.");
+
+#ifdef __APPLE__
+#pragma clang diagnostic pop
+#endif
+
 #endif
 
 
@@ -1483,9 +1552,19 @@ time_get_clock_info(PyObject *self, PyObject *args)
     }
 #ifdef HAVE_THREAD_TIME
     else if (strcmp(name, "thread_time") == 0) {
-        if (_PyTime_GetThreadTimeWithInfo(&t, &info) < 0) {
+
+#ifdef __APPLE__
+        if (HAVE_CLOCK_GETTIME_RUNTIME) {
+#endif
+            if (_PyTime_GetThreadTimeWithInfo(&t, &info) < 0) {
+                return NULL;
+            }
+#ifdef __APPLE__
+        } else {
+            PyErr_SetString(PyExc_ValueError, "unknown clock");
             return NULL;
         }
+#endif
     }
 #endif
     else {
@@ -1766,68 +1845,116 @@ if it is -1, mktime() should guess based on the date and time.\n");
 static int
 time_exec(PyObject *module)
 {
+#if defined(__APPLE__) && defined(HAVE_CLOCK_GETTIME)
+    if (HAVE_CLOCK_GETTIME_RUNTIME) {
+        /* pass: ^^^ cannot use '!' here */
+    } else {
+        PyObject* dct = PyModule_GetDict(module);
+        if (dct == NULL) {
+            return -1;
+        }
+
+        if (PyDict_DelItemString(dct, "clock_gettime") == -1) {
+            PyErr_Clear();
+        }
+        if (PyDict_DelItemString(dct, "clock_gettime_ns") == -1) {
+            PyErr_Clear();
+        }
+        if (PyDict_DelItemString(dct, "clock_settime") == -1) {
+            PyErr_Clear();
+        }
+        if (PyDict_DelItemString(dct, "clock_settime_ns") == -1) {
+            PyErr_Clear();
+        }
+        if (PyDict_DelItemString(dct, "clock_getres") == -1) {
+            PyErr_Clear();
+        }
+    }
+#endif
+#if defined(__APPLE__) && defined(HAVE_THREAD_TIME)
+    if (HAVE_CLOCK_GETTIME_RUNTIME) {
+        /* pass: ^^^ cannot use '!' here */
+    } else {
+        PyObject* dct = PyModule_GetDict(module);
+
+        if (PyDict_DelItemString(dct, "thread_time") == -1) {
+            PyErr_Clear();
+        }
+        if (PyDict_DelItemString(dct, "thread_time_ns") == -1) {
+            PyErr_Clear();
+        }
+    }
+#endif
     /* Set, or reset, module variables like time.timezone */
     if (init_timezone(module) < 0) {
         return -1;
     }
 
 #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_CLOCK_SETTIME) || defined(HAVE_CLOCK_GETRES)
+    if (HAVE_CLOCK_GETTIME_RUNTIME) {
 
 #ifdef CLOCK_REALTIME
-    if (PyModule_AddIntMacro(module, CLOCK_REALTIME) < 0) {
-        return -1;
-    }
+        if (PyModule_AddIntMacro(module, CLOCK_REALTIME) < 0) {
+            return -1;
+        }
 #endif
+
 #ifdef CLOCK_MONOTONIC
-    if (PyModule_AddIntMacro(module, CLOCK_MONOTONIC) < 0) {
-        return -1;
-    }
+
+        if (PyModule_AddIntMacro(module, CLOCK_MONOTONIC) < 0) {
+            return -1;
+        }
+
 #endif
 #ifdef CLOCK_MONOTONIC_RAW
-    if (PyModule_AddIntMacro(module, CLOCK_MONOTONIC_RAW) < 0) {
-        return -1;
-    }
+        if (PyModule_AddIntMacro(module, CLOCK_MONOTONIC_RAW) < 0) {
+            return -1;
+        }
 #endif
+
 #ifdef CLOCK_HIGHRES
-    if (PyModule_AddIntMacro(module, CLOCK_HIGHRES) < 0) {
-        return -1;
-    }
+        if (PyModule_AddIntMacro(module, CLOCK_HIGHRES) < 0) {
+            return -1;
+        }
 #endif
 #ifdef CLOCK_PROCESS_CPUTIME_ID
-    if (PyModule_AddIntMacro(module, CLOCK_PROCESS_CPUTIME_ID) < 0) {
-        return -1;
-    }
+        if (PyModule_AddIntMacro(module, CLOCK_PROCESS_CPUTIME_ID) < 0) {
+            return -1;
+        }
 #endif
+
 #ifdef CLOCK_THREAD_CPUTIME_ID
-    if (PyModule_AddIntMacro(module, CLOCK_THREAD_CPUTIME_ID) < 0) {
-        return -1;
-    }
+        if (PyModule_AddIntMacro(module, CLOCK_THREAD_CPUTIME_ID) < 0) {
+            return -1;
+        }
 #endif
 #ifdef CLOCK_PROF
-    if (PyModule_AddIntMacro(module, CLOCK_PROF) < 0) {
-        return -1;
-    }
+        if (PyModule_AddIntMacro(module, CLOCK_PROF) < 0) {
+            return -1;
+        }
 #endif
 #ifdef CLOCK_BOOTTIME
-    if (PyModule_AddIntMacro(module, CLOCK_BOOTTIME) < 0) {
-        return -1;
-    }
+        if (PyModule_AddIntMacro(module, CLOCK_BOOTTIME) < 0) {
+            return -1;
+        }
 #endif
 #ifdef CLOCK_TAI
-    if (PyModule_AddIntMacro(module, CLOCK_TAI) < 0) {
-        return -1;
-    }
+        if (PyModule_AddIntMacro(module, CLOCK_TAI) < 0) {
+            return -1;
+        }
 #endif
 #ifdef CLOCK_UPTIME
-    if (PyModule_AddIntMacro(module, CLOCK_UPTIME) < 0) {
-        return -1;
-    }
+        if (PyModule_AddIntMacro(module, CLOCK_UPTIME) < 0) {
+            return -1;
+        }
 #endif
 #ifdef CLOCK_UPTIME_RAW
-    if (PyModule_AddIntMacro(module, CLOCK_UPTIME_RAW) < 0) {
-        return -1;
-    }
+
+        if (PyModule_AddIntMacro(module, CLOCK_UPTIME_RAW) < 0) {
+            return -1;
+        }
 #endif
+    }
 
 #endif  /* defined(HAVE_CLOCK_GETTIME) || defined(HAVE_CLOCK_SETTIME) || defined(HAVE_CLOCK_GETRES) */
 
index 1d671c9..1922619 100644 (file)
@@ -2375,9 +2375,7 @@ abstract_get_bases(PyObject *cls)
     _Py_IDENTIFIER(__bases__);
     PyObject *bases;
 
-    Py_ALLOW_RECURSION
     (void)_PyObject_LookupAttrId(cls, &PyId___bases__, &bases);
-    Py_END_ALLOW_RECURSION
     if (bases != NULL && !PyTuple_Check(bases)) {
         Py_DECREF(bases);
         return NULL;
index bd24f67..2c60275 100644 (file)
@@ -20,9 +20,23 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
             return NULL;
     }
 
+    /* __module__: If module name is in globals, use it.
+       Otherwise, use None. */
+    module = PyDict_GetItemWithError(globals, __name__);
+    if (module) {
+        Py_INCREF(module);
+    }
+    else if (PyErr_Occurred()) {
+        return NULL;
+    }
+
     op = PyObject_GC_New(PyFunctionObject, &PyFunction_Type);
-    if (op == NULL)
+    if (op == NULL) {
+        Py_XDECREF(module);
         return NULL;
+    }
+    /* Note: No failures from this point on, since func_dealloc() does not
+       expect a partially-created object. */
 
     op->func_weakreflist = NULL;
     Py_INCREF(code);
@@ -35,6 +49,7 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
     op->func_kwdefaults = NULL; /* No keyword only defaults */
     op->func_closure = NULL;
     op->vectorcall = _PyFunction_Vectorcall;
+    op->func_module = module;
 
     consts = ((PyCodeObject *)code)->co_consts;
     if (PyTuple_Size(consts) >= 1) {
@@ -48,20 +63,8 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
     op->func_doc = doc;
 
     op->func_dict = NULL;
-    op->func_module = NULL;
     op->func_annotations = NULL;
 
-    /* __module__: If module name is in globals, use it.
-       Otherwise, use None. */
-    module = PyDict_GetItemWithError(globals, __name__);
-    if (module) {
-        Py_INCREF(module);
-        op->func_module = module;
-    }
-    else if (PyErr_Occurred()) {
-        Py_DECREF(op);
-        return NULL;
-    }
     if (qualname)
         op->func_qualname = qualname;
     else
index 4f95216..c5a81a5 100644 (file)
@@ -9,6 +9,7 @@ typedef struct {
     PyObject *origin;
     PyObject *args;
     PyObject *parameters;
+    PyObject* weakreflist;
 } gaobject;
 
 static void
@@ -17,6 +18,9 @@ ga_dealloc(PyObject *self)
     gaobject *alias = (gaobject *)self;
 
     _PyObject_GC_UNTRACK(self);
+    if (alias->weakreflist != NULL) {
+        PyObject_ClearWeakRefs((PyObject *)alias);
+    }
     Py_XDECREF(alias->origin);
     Py_XDECREF(alias->args);
     Py_XDECREF(alias->parameters);
@@ -562,7 +566,7 @@ static PyGetSetDef ga_properties[] = {
 static PyObject *
 ga_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 {
-    if (!_PyArg_NoKwnames("GenericAlias", kwds)) {
+    if (!_PyArg_NoKeywords("GenericAlias", kwds)) {
         return NULL;
     }
     if (!_PyArg_CheckPositional("GenericAlias", PyTuple_GET_SIZE(args), 2, 2)) {
@@ -582,7 +586,7 @@ PyTypeObject Py_GenericAliasType = {
     .tp_name = "types.GenericAlias",
     .tp_doc = "Represent a PEP 585 generic type\n"
               "\n"
-              "E.g. for t = list[int], t.origin is list and t.args is (int,).",
+              "E.g. for t = list[int], t.__origin__ is list and t.__args__ is (int,).",
     .tp_basicsize = sizeof(gaobject),
     .tp_dealloc = ga_dealloc,
     .tp_repr = ga_repr,
@@ -593,6 +597,7 @@ PyTypeObject Py_GenericAliasType = {
     .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
     .tp_traverse = ga_traverse,
     .tp_richcompare = ga_richcompare,
+    .tp_weaklistoffset = offsetof(gaobject, weakreflist),
     .tp_methods = ga_methods,
     .tp_members = ga_members,
     .tp_alloc = PyType_GenericAlloc,
@@ -624,6 +629,7 @@ Py_GenericAlias(PyObject *origin, PyObject *args)
     alias->origin = origin;
     alias->args = args;
     alias->parameters = NULL;
+    alias->weakreflist = NULL;
     _PyObject_GC_TRACK(alias);
     return (PyObject *)alias;
 }
index 5659f21..7b43041 100644 (file)
@@ -164,9 +164,11 @@ meth_dealloc(PyCFunctionObject *m)
     if (m->m_weakreflist != NULL) {
         PyObject_ClearWeakRefs((PyObject*) m);
     }
+    // Dereference class before m_self: PyCFunction_GET_CLASS accesses
+    // PyMethodDef m_ml, which could be kept alive by m_self
+    Py_XDECREF(PyCFunction_GET_CLASS(m));
     Py_XDECREF(m->m_self);
     Py_XDECREF(m->m_module);
-    Py_XDECREF(PyCFunction_GET_CLASS(m));
     PyObject_GC_Del(m);
 }
 
@@ -243,9 +245,9 @@ meth_get__qualname__(PyCFunctionObject *m, void *closure)
 static int
 meth_traverse(PyCFunctionObject *m, visitproc visit, void *arg)
 {
+    Py_VISIT(PyCFunction_GET_CLASS(m));
     Py_VISIT(m->m_self);
     Py_VISIT(m->m_module);
-    Py_VISIT(PyCFunction_GET_CLASS(m));
     return 0;
 }
 
index f65434f..acbe3fa 100644 (file)
@@ -2605,10 +2605,10 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
     slots = NULL;
 
     /* Initialize tp_flags */
+    // All heap types need GC, since we can create a reference cycle by storing
+    // an instance on one of its parents:
     type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE |
-        Py_TPFLAGS_BASETYPE;
-    if (base->tp_flags & Py_TPFLAGS_HAVE_GC)
-        type->tp_flags |= Py_TPFLAGS_HAVE_GC;
+        Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC;
 
     /* Initialize essential fields */
     type->tp_as_async = &et->as_async;
@@ -2808,21 +2808,11 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
     }
     type->tp_dealloc = subtype_dealloc;
 
-    /* Enable GC unless this class is not adding new instance variables and
-       the base class did not use GC. */
-    if ((base->tp_flags & Py_TPFLAGS_HAVE_GC) ||
-        type->tp_basicsize > base->tp_basicsize)
-        type->tp_flags |= Py_TPFLAGS_HAVE_GC;
-
     /* Always override allocation strategy to use regular heap */
     type->tp_alloc = PyType_GenericAlloc;
-    if (type->tp_flags & Py_TPFLAGS_HAVE_GC) {
-        type->tp_free = PyObject_GC_Del;
-        type->tp_traverse = subtype_traverse;
-        type->tp_clear = subtype_clear;
-    }
-    else
-        type->tp_free = PyObject_Del;
+    type->tp_free = PyObject_GC_Del;
+    type->tp_traverse = subtype_traverse;
+    type->tp_clear = subtype_clear;
 
     /* store type in class' cell if one is supplied */
     cell = _PyDict_GetItemIdWithError(dict, &PyId___classcell__);
@@ -2961,26 +2951,40 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases)
                 base = slot->pfunc;
             else if (slot->slot == Py_tp_bases) {
                 bases = slot->pfunc;
-                Py_INCREF(bases);
             }
         }
-        if (!bases)
+        if (!bases) {
             bases = PyTuple_Pack(1, base);
-        if (!bases)
+            if (!bases)
+                goto fail;
+        }
+        else if (!PyTuple_Check(bases)) {
+            PyErr_SetString(PyExc_SystemError, "Py_tp_bases is not a tuple");
             goto fail;
+        }
+        else {
+            Py_INCREF(bases);
+        }
     }
-    else
+    else if (!PyTuple_Check(bases)) {
+        PyErr_SetString(PyExc_SystemError, "bases is not a tuple");
+        goto fail;
+    }
+    else {
         Py_INCREF(bases);
+    }
 
     /* Calculate best base, and check that all bases are type objects */
     base = best_base(bases);
     if (base == NULL) {
+        Py_DECREF(bases);
         goto fail;
     }
     if (!_PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)) {
         PyErr_Format(PyExc_TypeError,
                      "type '%.100s' is not an acceptable base type",
                      base->tp_name);
+        Py_DECREF(bases);
         goto fail;
     }
 
@@ -2992,7 +2996,6 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases)
     type->tp_as_buffer = &res->as_buffer;
     /* Set tp_base and tp_bases */
     type->tp_bases = bases;
-    bases = NULL;
     Py_INCREF(base);
     type->tp_base = base;
 
index 4c8c880..ffd13f7 100644 (file)
@@ -847,7 +847,11 @@ xmlcharrefreplace(_PyBytesWriter *writer, char *str,
 
     /* generate replacement */
     for (i = collstart; i < collend; ++i) {
-        str += sprintf(str, "&#%d;", PyUnicode_READ(kind, data, i));
+        size = sprintf(str, "&#%d;", PyUnicode_READ(kind, data, i));
+        if (size < 0) {
+            return NULL;
+        }
+        str += size;
     }
     return str;
 }
@@ -1882,7 +1886,7 @@ _PyUnicode_Ready(PyObject *unicode)
         _PyUnicode_WSTR_LENGTH(unicode) = 0;
 #endif
     }
-    /* maxchar exeeds 16 bit, wee need 4 bytes for unicode characters */
+    /* maxchar exceeds 16 bit, wee need 4 bytes for unicode characters */
     else {
 #if SIZEOF_WCHAR_T == 2
         /* in case the native representation is 2-bytes, we need to allocate a
@@ -8160,7 +8164,7 @@ charmap_decode_mapping(const char *s,
                 goto Undefined;
             if (value < 0 || value > MAX_UNICODE) {
                 PyErr_Format(PyExc_TypeError,
-                             "character mapping must be in range(0x%lx)",
+                             "character mapping must be in range(0x%x)",
                              (unsigned long)MAX_UNICODE + 1);
                 goto onError;
             }
@@ -15602,9 +15606,7 @@ PyUnicode_InternInPlace(PyObject **p)
     }
 
     PyObject *t;
-    Py_ALLOW_RECURSION
     t = PyDict_SetDefault(interned, s, s);
-    Py_END_ALLOW_RECURSION
 
     if (t == NULL) {
         PyErr_Clear();
diff --git a/PC/icons/idlex150.png b/PC/icons/idlex150.png
new file mode 100644 (file)
index 0000000..806cb0c
Binary files /dev/null and b/PC/icons/idlex150.png differ
diff --git a/PC/icons/idlex44.png b/PC/icons/idlex44.png
new file mode 100644 (file)
index 0000000..3ef66e6
Binary files /dev/null and b/PC/icons/idlex44.png differ
index 9a7439d..747c97a 100644 (file)
@@ -67,8 +67,8 @@ PIP_VE_DATA = dict(
 IDLE_VE_DATA = dict(
     DisplayName="IDLE (Python {})".format(VER_DOT),
     Description="IDLE editor for Python {}".format(VER_DOT),
-    Square150x150Logo="_resources/pythonwx150.png",
-    Square44x44Logo="_resources/pythonwx44.png",
+    Square150x150Logo="_resources/idlex150.png",
+    Square44x44Logo="_resources/idlex44.png",
     BackgroundColor="transparent",
 )
 
@@ -498,6 +498,11 @@ def get_appx_layout(ns):
         src = icons / "pythonwx{}.png".format(px)
         yield f"_resources/pythonwx{px}.png", src
         yield f"_resources/pythonwx{px}$targetsize-{px}_altform-unplated.png", src
+    if ns.include_idle and ns.include_launchers:
+        for px in [44, 150]:
+            src = icons / "idlex{}.png".format(px)
+            yield f"_resources/idlex{px}.png", src
+            yield f"_resources/idlex{px}$targetsize-{px}_altform-unplated.png", src
     yield f"_resources/py.png", icons / "py.png"
     sccd = ns.source / SCCD_FILENAME
     if sccd.is_file():
index 02216b5..d7d3cf0 100644 (file)
@@ -193,7 +193,6 @@ typedef int pid_t;
 #define Py_IS_NAN _isnan
 #define Py_IS_INFINITY(X) (!_finite(X) && !_isnan(X))
 #define Py_IS_FINITE(X) _finite(X)
-#define copysign _copysign
 
 /* define some ANSI types that are not defined in earlier Win headers */
 #if _MSC_VER >= 1200
index 0edb067..a97b062 100644 (file)
@@ -46,7 +46,7 @@ echo.Available arguments:
 echo.  -c Release ^| Debug ^| PGInstrument ^| PGUpdate\r
 echo.     Set the configuration (default: Release)\r
 echo.  -p x64 ^| Win32 ^| ARM ^| ARM64\r
-echo.     Set the platform (default: Win32)\r
+echo.     Set the platform (default: x64)\r
 echo.  -t Build ^| Rebuild ^| Clean ^| CleanAll\r
 echo.     Set the target manually\r
 echo.  --pgo-job  The job to use for PGO training; implies --pgo\r
@@ -55,7 +55,7 @@ exit /b 127
 \r
 :Run\r
 setlocal\r
-set platf=Win32\r
+set platf=x64\r
 set conf=Release\r
 set target=Build\r
 set dir=%~dp0\r
index 2b2c005..31c67d4 100644 (file)
@@ -9,8 +9,19 @@ rem 'v110', 'v120' or 'v140') to the build script.
 \r
 echo Build environments: x86, amd64, x86_amd64\r
 echo.\r
-set VSTOOLS=%VS140COMNTOOLS%\r
-if "%VSTOOLS%"=="" set VSTOOLS=%VS120COMNTOOLS%\r
-if "%VSTOOLS%"=="" set VSTOOLS=%VS110COMNTOOLS%\r
-if "%VSTOOLS%"=="" set VSTOOLS=%VS100COMNTOOLS%\r
-call "%VSTOOLS%..\..\VC\vcvarsall.bat" %*\r
+set _ARGS=%*\r
+if NOT DEFINED _ARGS set _ARGS=amd64\r
+\r
+if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" goto :skip_vswhere\r
+set VSTOOLS=\r
+for /F "tokens=*" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -property installationPath -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64') DO @(set VSTOOLS=%%i\VC\Auxiliary\Build\vcvarsall.bat)\r
+if not defined VSTOOLS goto :skip_vswhere\r
+call "%VSTOOLS%" %_ARGS%\r
+exit /B 0\r
+\r
+:skip_vswhere\r
+if not defined VSTOOLS set VSTOOLS=%VS140COMNTOOLS%\r
+if not defined VSTOOLS set VSTOOLS=%VS120COMNTOOLS%\r
+if not defined VSTOOLS set VSTOOLS=%VS110COMNTOOLS%\r
+if not defined VSTOOLS set VSTOOLS=%VS100COMNTOOLS%\r
+call "%VSTOOLS%..\..\VC\vcvarsall.bat" %_ARGS%\r
diff --git a/PCbuild/env.ps1 b/PCbuild/env.ps1
new file mode 100644 (file)
index 0000000..8ba1fa4
--- /dev/null
@@ -0,0 +1,2 @@
+$pcbuild = $script:MyInvocation.MyCommand.Path | Split-Path -parent;\r
+& cmd /K "$pcbuild\env.bat" $args\r
index d0e4255..9dc84a6 100644 (file)
@@ -54,7 +54,7 @@ set libraries=
 set libraries=%libraries%                                       bzip2-1.0.6\r
 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries%  libffi\r
 if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries%     openssl-1.1.1g\r
-set libraries=%libraries%                                       sqlite-3.32.3.0\r
+set libraries=%libraries%                                       sqlite-3.33.0.0\r
 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.9.0\r
 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.9.0\r
 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tix-8.4.3.6\r
index bacaaa8..73eff5e 100644 (file)
@@ -4,12 +4,24 @@ rem Usage:  idle [-d]
 rem -d   Run Debug build (python_d.exe).  Else release build.\r
 \r
 setlocal\r
-set exe=win32\python\r
+set PCBUILD=%~dp0\r
+set exedir=%PCBUILD%\amd64\r
+set exe=python\r
 PATH %PATH%;..\externals\tcltk\bin\r
 \r
-if "%1"=="-d" (set exe=%exe%_d) & shift\r
+:CheckOpts\r
+if "%1"=="-d" (set exe=%exe%_d) & shift & goto :CheckOpts\r
+if "%1"=="-p" (call :SetExeDir %2) & shift & shift & goto :CheckOpts\r
 \r
-set cmd=%exe% ../Lib/idlelib/idle.py %1 %2 %3 %4 %5 %6 %7 %8 %9\r
+set cmd=%exedir%\%exe% %PCBUILD%\..\Lib\idlelib\idle.py %1 %2 %3 %4 %5 %6 %7 %8 %9\r
 \r
 echo on\r
 %cmd%\r
+exit /B %LASTERRORCODE%\r
+\r
+:SetExeDir\r
+if /I %1 EQU Win32 (set exedir=%PCBUILD%\win32)\r
+if /I %1 EQU x64 (set exedir=%PCBUILD%\amd64)\r
+if /I %1 EQU ARM (set exedir=%PCBUILD%\arm32)\r
+if /I %1 EQU ARM64 (set exedir=%PCBUILD%\arm64)\r
+exit /B 0\r
index a625eae..8380c64 100644 (file)
@@ -22,10 +22,10 @@ echo Based on https://github.com/libffi/libffi/blob/master/.appveyor.yml
 echo.\r
 echo.\r
 echo.Available flags:\r
-echo.  -x64    build for x64\r
-echo.  -x86    build for x86\r
-echo.  -arm32  build for arm32\r
-echo.  -arm64  build for arm64\r
+echo.  -x64    enable x64 build\r
+echo.  -x86    enable x86 build\r
+echo.  -arm32  enable arm32 build\r
+echo.  -arm64  enable arm64 build\r
 echo.  -?      this help\r
 echo.  --install-cygwin  install cygwin to c:\cygwin\r
 exit /b 127\r
@@ -44,6 +44,7 @@ set INSTALL_CYGWIN=
 if "%1"=="" goto :CheckOptsDone\r
 if /I "%1"=="-x64" (set BUILD_X64=1) & shift & goto :CheckOpts\r
 if /I "%1"=="-x86" (set BUILD_X86=1) & shift & goto :CheckOpts\r
+if /I "%1"=="-win32" (set BUILD_X86=1) & shift & goto :CheckOpts\r
 if /I "%1"=="-arm32" (set BUILD_ARM32=1) & shift & goto :CheckOpts\r
 if /I "%1"=="-arm64" (set BUILD_ARM64=1) & shift & goto :CheckOpts\r
 if /I "%1"=="-pdb" (set BUILD_PDB=-g) & shift & goto :CheckOpts\r
@@ -67,9 +68,7 @@ setlocal
 if NOT DEFINED SH if exist c:\cygwin\bin\sh.exe set SH=c:\cygwin\bin\sh.exe\r
 \r
 if NOT DEFINED VCVARSALL (\r
-    if exist "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" (\r
-        set VCVARSALL="C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat"\r
-    )\r
+    for /F "tokens=*" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -property installationPath -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64') DO @(set VCVARSALL="%%i\VC\Auxiliary\Build\vcvarsall.bat")\r
 )\r
 if ^%VCVARSALL:~0,1% NEQ ^" SET VCVARSALL="%VCVARSALL%"\r
 \r
index 9188414..95649ea 100644 (file)
@@ -176,8 +176,8 @@ public override bool Execute() {
     <SdkBinPath Condition="!Exists($(SdkBinPath))">$(registry:HKEY_LOCAL_MACHINE\Software\Microsoft\Windows Kits\Installed Roots@KitsRoot81)\bin\x86</SdkBinPath>\r
     <SdkBinPath Condition="!Exists($(SdkBinPath))">$(registry:HKEY_LOCAL_MACHINE\Software\Microsoft\Windows Kits\Installed Roots@KitsRoot)\bin\x86</SdkBinPath>\r
     <SdkBinPath Condition="!Exists($(SdkBinPath))">$(registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1A@InstallationFolder)\Bin\</SdkBinPath>\r
-    <_SignCommand Condition="Exists($(SdkBinPath)) and '$(SigningCertificate)' != '' and $(SupportSigning)">"$(SdkBinPath)\signtool.exe" sign /a /n "$(SigningCertificate)" /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d "Python $(PythonVersion)"</_SignCommand>\r
-    <_SignCommand Condition="Exists($(SdkBinPath)) and '$(SigningCertificateSha1)' != '' and $(SupportSigning)">"$(SdkBinPath)\signtool.exe" sign /a /sha1 "$(SigningCertificateSha1)" /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d "Python $(PythonVersion)"</_SignCommand>\r
+    <_SignCommand Condition="Exists($(SdkBinPath)) and '$(SigningCertificate)' != '' and $(SupportSigning)">"$(SdkBinPath)\signtool.exe" sign /a /n "$(SigningCertificate)" /fd sha256 /tr http://timestamp.digicert.com/ /td sha256 /d "Python $(PythonVersion)"</_SignCommand>\r
+    <_SignCommand Condition="Exists($(SdkBinPath)) and '$(SigningCertificateSha1)' != '' and $(SupportSigning)">"$(SdkBinPath)\signtool.exe" sign /a /sha1 "$(SigningCertificateSha1)" /fd sha256 /tr http://timestamp.digicert.com/ /td sha256 /d "Python $(PythonVersion)"</_SignCommand>\r
     <_MakeCatCommand Condition="Exists($(SdkBinPath))">"$(SdkBinPath)\makecat.exe"</_MakeCatCommand>\r
   </PropertyGroup>\r
 \r
index fe59405..3a3a787 100644 (file)
@@ -56,7 +56,7 @@
     <ExternalsDir>$(EXTERNALS_DIR)</ExternalsDir>\r
     <ExternalsDir Condition="$(ExternalsDir) == ''">$([System.IO.Path]::GetFullPath(`$(PySourcePath)externals`))</ExternalsDir>\r
     <ExternalsDir Condition="!HasTrailingSlash($(ExternalsDir))">$(ExternalsDir)\</ExternalsDir>\r
-    <sqlite3Dir>$(ExternalsDir)sqlite-3.32.3.0\</sqlite3Dir>\r
+    <sqlite3Dir>$(ExternalsDir)sqlite-3.33.0.0\</sqlite3Dir>\r
     <bz2Dir>$(ExternalsDir)bzip2-1.0.6\</bz2Dir>\r
     <lzmaDir>$(ExternalsDir)xz-5.2.2\</lzmaDir>\r
     <libffiDir>$(ExternalsDir)libffi\</libffiDir>\r
index 92b8c8c..e07cdd2 100644 (file)
@@ -185,7 +185,7 @@ _ssl
     again when building.\r
 \r
 _sqlite3\r
-    Wraps SQLite 3.32.3.0, which is itself built by sqlite3.vcxproj\r
+    Wraps SQLite 3.33.0, which is itself built by sqlite3.vcxproj\r
     Homepage:\r
         http://www.sqlite.org/\r
 _tkinter\r
index 3773eea..f15e3e2 100644 (file)
@@ -6,8 +6,9 @@ rem -O   Run python.exe or python_d.exe (see -d) with -O.
 rem -q   "quick" -- normally the tests are run twice, the first time\r
 rem      after deleting all the .pyc files reachable from Lib/.\r
 rem      -q runs the tests just once, and without deleting .pyc files.\r
-rem -x64 Run the 64-bit build of python (or python_d if -d was specified)\r
-rem      When omitted, uses %PREFIX% if set or the 32-bit build\r
+rem -p <Win32|x64|ARM|ARM64> or -win32, -x64, -arm32, -arm64\r
+rem      Run the specified architecture of python (or python_d if -d\r
+rem      was specified). If omitted, uses %PREFIX% if set or 64-bit.\r
 rem All leading instances of these switches are shifted off, and\r
 rem whatever remains (up to 9 arguments) is passed to regrtest.py.\r
 rem For example,\r
@@ -38,12 +39,14 @@ set exe=
 if "%1"=="-O" (set dashO=-O)     & shift & goto CheckOpts\r
 if "%1"=="-q" (set qmode=yes)    & shift & goto CheckOpts\r
 if "%1"=="-d" (set suffix=_d)    & shift & goto CheckOpts\r
+if "%1"=="-win32" (set prefix=%pcbuild%win32) & shift & goto CheckOpts\r
 if "%1"=="-x64" (set prefix=%pcbuild%amd64) & shift & goto CheckOpts\r
 if "%1"=="-arm64" (set prefix=%pcbuild%arm64) & shift & goto CheckOpts\r
 if "%1"=="-arm32" (set prefix=%pcbuild%arm32) & shift & goto CheckOpts\r
+if "%1"=="-p" (call :SetPlatform %~2) & shift & shift & goto CheckOpts\r
 if NOT "%1"=="" (set regrtestargs=%regrtestargs% %1) & shift & goto CheckOpts\r
 \r
-if not defined prefix set prefix=%pcbuild%win32\r
+if not defined prefix set prefix=%pcbuild%amd64\r
 set exe=%prefix%\python%suffix%.exe\r
 set cmd="%exe%" %dashO% -u -Wd -E -bb -m test %regrtestargs%\r
 if defined qmode goto Qmode\r
@@ -60,6 +63,15 @@ echo on
 \r
 echo About to run again without deleting .pyc first:\r
 pause\r
+goto Qmode\r
+\r
+:SetPlatform\r
+if /I %1 EQU Win32 (set prefix=%pcbuild%win32) & exit /B 0\r
+if /I %1 EQU x64 (set prefix=%pcbuild%amd64) & exit /B 0\r
+if /I %1 EQU ARM64 (set prefix=%pcbuild%arm64) & exit /B 0\r
+if /I %1 EQU ARM (set prefix=%pcbuild%arm32) & exit /B 0\r
+echo Invalid platform "%1"\r
+exit /B 1\r
 \r
 :Qmode\r
 echo on\r
index f74b6f2..0eb61db 100644 (file)
@@ -134,97 +134,97 @@ static KeywordToken *reserved_keywords[] = {
 #define class_def_type 1056
 #define class_def_raw_type 1057
 #define block_type 1058
-#define expressions_list_type 1059
-#define star_expressions_type 1060
-#define star_expression_type 1061
-#define star_named_expressions_type 1062
-#define star_named_expression_type 1063
-#define named_expression_type 1064
-#define annotated_rhs_type 1065
-#define expressions_type 1066
-#define expression_type 1067
-#define lambdef_type 1068
-#define lambda_params_type 1069
-#define lambda_parameters_type 1070
-#define lambda_slash_no_default_type 1071
-#define lambda_slash_with_default_type 1072
-#define lambda_star_etc_type 1073
-#define lambda_kwds_type 1074
-#define lambda_param_no_default_type 1075
-#define lambda_param_with_default_type 1076
-#define lambda_param_maybe_default_type 1077
-#define lambda_param_type 1078
-#define disjunction_type 1079
-#define conjunction_type 1080
-#define inversion_type 1081
-#define comparison_type 1082
-#define compare_op_bitwise_or_pair_type 1083
-#define eq_bitwise_or_type 1084
-#define noteq_bitwise_or_type 1085
-#define lte_bitwise_or_type 1086
-#define lt_bitwise_or_type 1087
-#define gte_bitwise_or_type 1088
-#define gt_bitwise_or_type 1089
-#define notin_bitwise_or_type 1090
-#define in_bitwise_or_type 1091
-#define isnot_bitwise_or_type 1092
-#define is_bitwise_or_type 1093
-#define bitwise_or_type 1094  // Left-recursive
-#define bitwise_xor_type 1095  // Left-recursive
-#define bitwise_and_type 1096  // Left-recursive
-#define shift_expr_type 1097  // Left-recursive
-#define sum_type 1098  // Left-recursive
-#define term_type 1099  // Left-recursive
-#define factor_type 1100
-#define power_type 1101
-#define await_primary_type 1102
-#define primary_type 1103  // Left-recursive
-#define slices_type 1104
-#define slice_type 1105
-#define atom_type 1106
-#define strings_type 1107
-#define list_type 1108
-#define listcomp_type 1109
-#define tuple_type 1110
-#define group_type 1111
-#define genexp_type 1112
-#define set_type 1113
-#define setcomp_type 1114
-#define dict_type 1115
-#define dictcomp_type 1116
-#define double_starred_kvpairs_type 1117
-#define double_starred_kvpair_type 1118
-#define kvpair_type 1119
-#define for_if_clauses_type 1120
-#define for_if_clause_type 1121
-#define yield_expr_type 1122
-#define arguments_type 1123
-#define args_type 1124
-#define kwargs_type 1125
-#define starred_expression_type 1126
-#define kwarg_or_starred_type 1127
-#define kwarg_or_double_starred_type 1128
-#define star_targets_type 1129
-#define star_targets_seq_type 1130
-#define star_target_type 1131
-#define star_atom_type 1132
-#define single_target_type 1133
-#define single_subscript_attribute_target_type 1134
-#define del_targets_type 1135
-#define del_target_type 1136
-#define del_t_atom_type 1137
-#define targets_type 1138
-#define target_type 1139
-#define t_primary_type 1140  // Left-recursive
-#define t_lookahead_type 1141
-#define t_atom_type 1142
-#define incorrect_arguments_type 1143
-#define invalid_kwarg_type 1144
-#define invalid_named_expression_type 1145
-#define invalid_assignment_type 1146
-#define invalid_ann_assign_target_type 1147
-#define invalid_del_stmt_type 1148
-#define invalid_block_type 1149
+#define star_expressions_type 1059
+#define star_expression_type 1060
+#define star_named_expressions_type 1061
+#define star_named_expression_type 1062
+#define named_expression_type 1063
+#define annotated_rhs_type 1064
+#define expressions_type 1065
+#define expression_type 1066
+#define lambdef_type 1067
+#define lambda_params_type 1068
+#define lambda_parameters_type 1069
+#define lambda_slash_no_default_type 1070
+#define lambda_slash_with_default_type 1071
+#define lambda_star_etc_type 1072
+#define lambda_kwds_type 1073
+#define lambda_param_no_default_type 1074
+#define lambda_param_with_default_type 1075
+#define lambda_param_maybe_default_type 1076
+#define lambda_param_type 1077
+#define disjunction_type 1078
+#define conjunction_type 1079
+#define inversion_type 1080
+#define comparison_type 1081
+#define compare_op_bitwise_or_pair_type 1082
+#define eq_bitwise_or_type 1083
+#define noteq_bitwise_or_type 1084
+#define lte_bitwise_or_type 1085
+#define lt_bitwise_or_type 1086
+#define gte_bitwise_or_type 1087
+#define gt_bitwise_or_type 1088
+#define notin_bitwise_or_type 1089
+#define in_bitwise_or_type 1090
+#define isnot_bitwise_or_type 1091
+#define is_bitwise_or_type 1092
+#define bitwise_or_type 1093  // Left-recursive
+#define bitwise_xor_type 1094  // Left-recursive
+#define bitwise_and_type 1095  // Left-recursive
+#define shift_expr_type 1096  // Left-recursive
+#define sum_type 1097  // Left-recursive
+#define term_type 1098  // Left-recursive
+#define factor_type 1099
+#define power_type 1100
+#define await_primary_type 1101
+#define primary_type 1102  // Left-recursive
+#define slices_type 1103
+#define slice_type 1104
+#define atom_type 1105
+#define strings_type 1106
+#define list_type 1107
+#define listcomp_type 1108
+#define tuple_type 1109
+#define group_type 1110
+#define genexp_type 1111
+#define set_type 1112
+#define setcomp_type 1113
+#define dict_type 1114
+#define dictcomp_type 1115
+#define double_starred_kvpairs_type 1116
+#define double_starred_kvpair_type 1117
+#define kvpair_type 1118
+#define for_if_clauses_type 1119
+#define for_if_clause_type 1120
+#define yield_expr_type 1121
+#define arguments_type 1122
+#define args_type 1123
+#define kwargs_type 1124
+#define starred_expression_type 1125
+#define kwarg_or_starred_type 1126
+#define kwarg_or_double_starred_type 1127
+#define star_targets_type 1128
+#define star_targets_seq_type 1129
+#define star_target_type 1130
+#define star_atom_type 1131
+#define single_target_type 1132
+#define single_subscript_attribute_target_type 1133
+#define del_targets_type 1134
+#define del_target_type 1135
+#define del_t_atom_type 1136
+#define targets_type 1137
+#define target_type 1138
+#define t_primary_type 1139  // Left-recursive
+#define t_lookahead_type 1140
+#define t_atom_type 1141
+#define invalid_arguments_type 1142
+#define invalid_kwarg_type 1143
+#define invalid_named_expression_type 1144
+#define invalid_assignment_type 1145
+#define invalid_ann_assign_target_type 1146
+#define invalid_del_stmt_type 1147
+#define invalid_block_type 1148
+#define invalid_primary_type 1149  // Left-recursive
 #define invalid_comprehension_type 1150
 #define invalid_dict_comprehension_type 1151
 #define invalid_parameters_type 1152
@@ -305,72 +305,72 @@ static KeywordToken *reserved_keywords[] = {
 #define _loop1_67_type 1227
 #define _loop1_68_type 1228
 #define _tmp_69_type 1229
-#define _loop0_71_type 1230
-#define _gather_70_type 1231
-#define _loop1_72_type 1232
-#define _loop0_74_type 1233
-#define _gather_73_type 1234
-#define _loop1_75_type 1235
+#define _loop1_70_type 1230
+#define _loop0_72_type 1231
+#define _gather_71_type 1232
+#define _loop1_73_type 1233
+#define _loop0_74_type 1234
+#define _loop0_75_type 1235
 #define _loop0_76_type 1236
-#define _loop0_77_type 1237
+#define _loop1_77_type 1237
 #define _loop0_78_type 1238
 #define _loop1_79_type 1239
-#define _loop0_80_type 1240
+#define _loop1_80_type 1240
 #define _loop1_81_type 1241
-#define _loop1_82_type 1242
+#define _loop0_82_type 1242
 #define _loop1_83_type 1243
 #define _loop0_84_type 1244
 #define _loop1_85_type 1245
 #define _loop0_86_type 1246
 #define _loop1_87_type 1247
-#define _loop0_88_type 1248
+#define _loop1_88_type 1248
 #define _loop1_89_type 1249
 #define _loop1_90_type 1250
-#define _loop1_91_type 1251
-#define _loop1_92_type 1252
-#define _tmp_93_type 1253
-#define _loop0_95_type 1254
-#define _gather_94_type 1255
+#define _tmp_91_type 1251
+#define _loop0_93_type 1252
+#define _gather_92_type 1253
+#define _tmp_94_type 1254
+#define _tmp_95_type 1255
 #define _tmp_96_type 1256
 #define _tmp_97_type 1257
-#define _tmp_98_type 1258
+#define _loop1_98_type 1258
 #define _tmp_99_type 1259
-#define _loop1_100_type 1260
-#define _tmp_101_type 1261
-#define _tmp_102_type 1262
-#define _loop0_104_type 1263
-#define _gather_103_type 1264
-#define _loop1_105_type 1265
-#define _loop0_106_type 1266
-#define _loop0_107_type 1267
-#define _loop0_109_type 1268
-#define _gather_108_type 1269
-#define _tmp_110_type 1270
+#define _tmp_100_type 1260
+#define _loop0_102_type 1261
+#define _gather_101_type 1262
+#define _loop1_103_type 1263
+#define _loop0_104_type 1264
+#define _loop0_105_type 1265
+#define _loop0_107_type 1266
+#define _gather_106_type 1267
+#define _tmp_108_type 1268
+#define _loop0_110_type 1269
+#define _gather_109_type 1270
 #define _loop0_112_type 1271
 #define _gather_111_type 1272
 #define _loop0_114_type 1273
 #define _gather_113_type 1274
 #define _loop0_116_type 1275
 #define _gather_115_type 1276
-#define _loop0_118_type 1277
-#define _gather_117_type 1278
-#define _loop0_119_type 1279
-#define _loop0_121_type 1280
-#define _gather_120_type 1281
-#define _tmp_122_type 1282
+#define _loop0_117_type 1277
+#define _loop0_119_type 1278
+#define _gather_118_type 1279
+#define _tmp_120_type 1280
+#define _loop0_122_type 1281
+#define _gather_121_type 1282
 #define _loop0_124_type 1283
 #define _gather_123_type 1284
-#define _loop0_126_type 1285
-#define _gather_125_type 1286
-#define _tmp_127_type 1287
+#define _tmp_125_type 1285
+#define _loop0_126_type 1286
+#define _loop0_127_type 1287
 #define _loop0_128_type 1288
-#define _loop0_129_type 1289
-#define _loop0_130_type 1290
-#define _tmp_131_type 1291
+#define _tmp_129_type 1289
+#define _tmp_130_type 1290
+#define _loop0_131_type 1291
 #define _tmp_132_type 1292
 #define _loop0_133_type 1293
 #define _tmp_134_type 1294
-#define _loop0_135_type 1295
+#define _tmp_135_type 1295
 #define _tmp_136_type 1296
 #define _tmp_137_type 1297
 #define _tmp_138_type 1298
@@ -386,12 +386,10 @@ static KeywordToken *reserved_keywords[] = {
 #define _tmp_148_type 1308
 #define _tmp_149_type 1309
 #define _tmp_150_type 1310
-#define _tmp_151_type 1311
-#define _tmp_152_type 1312
-#define _loop1_153_type 1313
-#define _loop1_154_type 1314
-#define _tmp_155_type 1315
-#define _tmp_156_type 1316
+#define _loop1_151_type 1311
+#define _loop1_152_type 1312
+#define _tmp_153_type 1313
+#define _tmp_154_type 1314
 
 static mod_ty file_rule(Parser *p);
 static mod_ty interactive_rule(Parser *p);
@@ -452,7 +450,6 @@ static asdl_seq* decorators_rule(Parser *p);
 static stmt_ty class_def_rule(Parser *p);
 static stmt_ty class_def_raw_rule(Parser *p);
 static asdl_seq* block_rule(Parser *p);
-static asdl_seq* expressions_list_rule(Parser *p);
 static expr_ty star_expressions_rule(Parser *p);
 static expr_ty star_expression_rule(Parser *p);
 static asdl_seq* star_named_expressions_rule(Parser *p);
@@ -536,13 +533,14 @@ static expr_ty target_rule(Parser *p);
 static expr_ty t_primary_rule(Parser *p);
 static void *t_lookahead_rule(Parser *p);
 static expr_ty t_atom_rule(Parser *p);
-static void *incorrect_arguments_rule(Parser *p);
+static void *invalid_arguments_rule(Parser *p);
 static void *invalid_kwarg_rule(Parser *p);
 static void *invalid_named_expression_rule(Parser *p);
 static void *invalid_assignment_rule(Parser *p);
 static expr_ty invalid_ann_assign_target_rule(Parser *p);
 static void *invalid_del_stmt_rule(Parser *p);
 static void *invalid_block_rule(Parser *p);
+static void *invalid_primary_rule(Parser *p);
 static void *invalid_comprehension_rule(Parser *p);
 static void *invalid_dict_comprehension_rule(Parser *p);
 static void *invalid_parameters_rule(Parser *p);
@@ -623,72 +621,72 @@ static asdl_seq *_loop0_66_rule(Parser *p);
 static asdl_seq *_loop1_67_rule(Parser *p);
 static asdl_seq *_loop1_68_rule(Parser *p);
 static void *_tmp_69_rule(Parser *p);
-static asdl_seq *_loop0_71_rule(Parser *p);
-static asdl_seq *_gather_70_rule(Parser *p);
-static asdl_seq *_loop1_72_rule(Parser *p);
+static asdl_seq *_loop1_70_rule(Parser *p);
+static asdl_seq *_loop0_72_rule(Parser *p);
+static asdl_seq *_gather_71_rule(Parser *p);
+static asdl_seq *_loop1_73_rule(Parser *p);
 static asdl_seq *_loop0_74_rule(Parser *p);
-static asdl_seq *_gather_73_rule(Parser *p);
-static asdl_seq *_loop1_75_rule(Parser *p);
+static asdl_seq *_loop0_75_rule(Parser *p);
 static asdl_seq *_loop0_76_rule(Parser *p);
-static asdl_seq *_loop0_77_rule(Parser *p);
+static asdl_seq *_loop1_77_rule(Parser *p);
 static asdl_seq *_loop0_78_rule(Parser *p);
 static asdl_seq *_loop1_79_rule(Parser *p);
-static asdl_seq *_loop0_80_rule(Parser *p);
+static asdl_seq *_loop1_80_rule(Parser *p);
 static asdl_seq *_loop1_81_rule(Parser *p);
-static asdl_seq *_loop1_82_rule(Parser *p);
+static asdl_seq *_loop0_82_rule(Parser *p);
 static asdl_seq *_loop1_83_rule(Parser *p);
 static asdl_seq *_loop0_84_rule(Parser *p);
 static asdl_seq *_loop1_85_rule(Parser *p);
 static asdl_seq *_loop0_86_rule(Parser *p);
 static asdl_seq *_loop1_87_rule(Parser *p);
-static asdl_seq *_loop0_88_rule(Parser *p);
+static asdl_seq *_loop1_88_rule(Parser *p);
 static asdl_seq *_loop1_89_rule(Parser *p);
 static asdl_seq *_loop1_90_rule(Parser *p);
-static asdl_seq *_loop1_91_rule(Parser *p);
-static asdl_seq *_loop1_92_rule(Parser *p);
-static void *_tmp_93_rule(Parser *p);
-static asdl_seq *_loop0_95_rule(Parser *p);
-static asdl_seq *_gather_94_rule(Parser *p);
+static void *_tmp_91_rule(Parser *p);
+static asdl_seq *_loop0_93_rule(Parser *p);
+static asdl_seq *_gather_92_rule(Parser *p);
+static void *_tmp_94_rule(Parser *p);
+static void *_tmp_95_rule(Parser *p);
 static void *_tmp_96_rule(Parser *p);
 static void *_tmp_97_rule(Parser *p);
-static void *_tmp_98_rule(Parser *p);
+static asdl_seq *_loop1_98_rule(Parser *p);
 static void *_tmp_99_rule(Parser *p);
-static asdl_seq *_loop1_100_rule(Parser *p);
-static void *_tmp_101_rule(Parser *p);
-static void *_tmp_102_rule(Parser *p);
+static void *_tmp_100_rule(Parser *p);
+static asdl_seq *_loop0_102_rule(Parser *p);
+static asdl_seq *_gather_101_rule(Parser *p);
+static asdl_seq *_loop1_103_rule(Parser *p);
 static asdl_seq *_loop0_104_rule(Parser *p);
-static asdl_seq *_gather_103_rule(Parser *p);
-static asdl_seq *_loop1_105_rule(Parser *p);
-static asdl_seq *_loop0_106_rule(Parser *p);
+static asdl_seq *_loop0_105_rule(Parser *p);
 static asdl_seq *_loop0_107_rule(Parser *p);
-static asdl_seq *_loop0_109_rule(Parser *p);
-static asdl_seq *_gather_108_rule(Parser *p);
-static void *_tmp_110_rule(Parser *p);
+static asdl_seq *_gather_106_rule(Parser *p);
+static void *_tmp_108_rule(Parser *p);
+static asdl_seq *_loop0_110_rule(Parser *p);
+static asdl_seq *_gather_109_rule(Parser *p);
 static asdl_seq *_loop0_112_rule(Parser *p);
 static asdl_seq *_gather_111_rule(Parser *p);
 static asdl_seq *_loop0_114_rule(Parser *p);
 static asdl_seq *_gather_113_rule(Parser *p);
 static asdl_seq *_loop0_116_rule(Parser *p);
 static asdl_seq *_gather_115_rule(Parser *p);
-static asdl_seq *_loop0_118_rule(Parser *p);
-static asdl_seq *_gather_117_rule(Parser *p);
+static asdl_seq *_loop0_117_rule(Parser *p);
 static asdl_seq *_loop0_119_rule(Parser *p);
-static asdl_seq *_loop0_121_rule(Parser *p);
-static asdl_seq *_gather_120_rule(Parser *p);
-static void *_tmp_122_rule(Parser *p);
+static asdl_seq *_gather_118_rule(Parser *p);
+static void *_tmp_120_rule(Parser *p);
+static asdl_seq *_loop0_122_rule(Parser *p);
+static asdl_seq *_gather_121_rule(Parser *p);
 static asdl_seq *_loop0_124_rule(Parser *p);
 static asdl_seq *_gather_123_rule(Parser *p);
+static void *_tmp_125_rule(Parser *p);
 static asdl_seq *_loop0_126_rule(Parser *p);
-static asdl_seq *_gather_125_rule(Parser *p);
-static void *_tmp_127_rule(Parser *p);
+static asdl_seq *_loop0_127_rule(Parser *p);
 static asdl_seq *_loop0_128_rule(Parser *p);
-static asdl_seq *_loop0_129_rule(Parser *p);
-static asdl_seq *_loop0_130_rule(Parser *p);
-static void *_tmp_131_rule(Parser *p);
+static void *_tmp_129_rule(Parser *p);
+static void *_tmp_130_rule(Parser *p);
+static asdl_seq *_loop0_131_rule(Parser *p);
 static void *_tmp_132_rule(Parser *p);
 static asdl_seq *_loop0_133_rule(Parser *p);
 static void *_tmp_134_rule(Parser *p);
-static asdl_seq *_loop0_135_rule(Parser *p);
+static void *_tmp_135_rule(Parser *p);
 static void *_tmp_136_rule(Parser *p);
 static void *_tmp_137_rule(Parser *p);
 static void *_tmp_138_rule(Parser *p);
@@ -704,12 +702,10 @@ static void *_tmp_147_rule(Parser *p);
 static void *_tmp_148_rule(Parser *p);
 static void *_tmp_149_rule(Parser *p);
 static void *_tmp_150_rule(Parser *p);
-static void *_tmp_151_rule(Parser *p);
-static void *_tmp_152_rule(Parser *p);
-static asdl_seq *_loop1_153_rule(Parser *p);
-static asdl_seq *_loop1_154_rule(Parser *p);
-static void *_tmp_155_rule(Parser *p);
-static void *_tmp_156_rule(Parser *p);
+static asdl_seq *_loop1_151_rule(Parser *p);
+static asdl_seq *_loop1_152_rule(Parser *p);
+static void *_tmp_153_rule(Parser *p);
+static void *_tmp_154_rule(Parser *p);
 
 
 // file: statements? $
@@ -2217,7 +2213,7 @@ assignment_rule(Parser *p)
             return NULL;
         }
     }
-    { // invalid_assignment
+    if (p->call_invalid_rules) { // invalid_assignment
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
@@ -2890,7 +2886,7 @@ del_stmt_rule(Parser *p)
         D(fprintf(stderr, "%*c%s del_stmt[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'del' del_targets &(';' | NEWLINE)"));
     }
-    { // invalid_del_stmt
+    if (p->call_invalid_rules) { // invalid_del_stmt
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
@@ -3241,7 +3237,7 @@ import_from_targets_rule(Parser *p)
         D(fprintf(stderr, "%*c%s import_from_targets[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'*'"));
     }
-    { // invalid_import_from_targets
+    if (p->call_invalid_rules) { // invalid_import_from_targets
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
@@ -3458,6 +3454,8 @@ dotted_name_rule(Parser *p)
         }
         p->mark = _mark;
         void *_raw = dotted_name_raw(p);
+        if (p->error_indicator)
+            return NULL;
         if (_raw == NULL || p->mark <= _resmark)
             break;
         _resmark = p->mark;
@@ -4034,7 +4032,7 @@ for_stmt_rule(Parser *p)
             return NULL;
         }
     }
-    { // invalid_for_target
+    if (p->call_invalid_rules) { // invalid_for_target
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
@@ -4289,7 +4287,10 @@ with_stmt_rule(Parser *p)
     return _res;
 }
 
-// with_item: expression 'as' target &(',' | ')' | ':') | invalid_with_item | expression
+// with_item:
+//     | expression 'as' star_target &(',' | ')' | ':')
+//     | invalid_with_item
+//     | expression
 static withitem_ty
 with_item_rule(Parser *p)
 {
@@ -4300,12 +4301,12 @@ with_item_rule(Parser *p)
     }
     withitem_ty _res = NULL;
     int _mark = p->mark;
-    { // expression 'as' target &(',' | ')' | ':')
+    { // expression 'as' star_target &(',' | ')' | ':')
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> with_item[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression 'as' target &(',' | ')' | ':')"));
+        D(fprintf(stderr, "%*c> with_item[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression 'as' star_target &(',' | ')' | ':')"));
         Token * _keyword;
         expr_ty e;
         expr_ty t;
@@ -4314,12 +4315,12 @@ with_item_rule(Parser *p)
             &&
             (_keyword = _PyPegen_expect_token(p, 520))  // token='as'
             &&
-            (t = target_rule(p))  // target
+            (t = star_target_rule(p))  // star_target
             &&
             _PyPegen_lookahead(1, _tmp_47_rule, p)
         )
         {
-            D(fprintf(stderr, "%*c+ with_item[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression 'as' target &(',' | ')' | ':')"));
+            D(fprintf(stderr, "%*c+ with_item[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression 'as' star_target &(',' | ')' | ':')"));
             _res = _Py_withitem ( e , t , p -> arena );
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -4330,9 +4331,9 @@ with_item_rule(Parser *p)
         }
         p->mark = _mark;
         D(fprintf(stderr, "%*c%s with_item[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression 'as' target &(',' | ')' | ':')"));
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression 'as' star_target &(',' | ')' | ':')"));
     }
-    { // invalid_with_item
+    if (p->call_invalid_rules) { // invalid_with_item
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
@@ -5067,7 +5068,7 @@ func_type_comment_rule(Parser *p)
         D(fprintf(stderr, "%*c%s func_type_comment[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NEWLINE TYPE_COMMENT &(NEWLINE INDENT)"));
     }
-    { // invalid_double_type_comments
+    if (p->call_invalid_rules) { // invalid_double_type_comments
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
@@ -5122,7 +5123,7 @@ params_rule(Parser *p)
     }
     arguments_ty _res = NULL;
     int _mark = p->mark;
-    { // invalid_parameters
+    if (p->call_invalid_rules) { // invalid_parameters
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
@@ -5597,7 +5598,7 @@ star_etc_rule(Parser *p)
         D(fprintf(stderr, "%*c%s star_etc[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwds"));
     }
-    { // invalid_star_etc
+    if (p->call_invalid_rules) { // invalid_star_etc
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
@@ -6300,7 +6301,7 @@ block_rule(Parser *p)
         D(fprintf(stderr, "%*c%s block[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "simple_stmt"));
     }
-    { // invalid_block
+    if (p->call_invalid_rules) { // invalid_block
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
@@ -6326,51 +6327,6 @@ block_rule(Parser *p)
     return _res;
 }
 
-// expressions_list: ','.star_expression+ ','?
-static asdl_seq*
-expressions_list_rule(Parser *p)
-{
-    D(p->level++);
-    if (p->error_indicator) {
-        D(p->level--);
-        return NULL;
-    }
-    asdl_seq* _res = NULL;
-    int _mark = p->mark;
-    { // ','.star_expression+ ','?
-        if (p->error_indicator) {
-            D(p->level--);
-            return NULL;
-        }
-        D(fprintf(stderr, "%*c> expressions_list[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.star_expression+ ','?"));
-        void *_opt_var;
-        UNUSED(_opt_var); // Silence compiler warnings
-        asdl_seq * a;
-        if (
-            (a = _gather_70_rule(p))  // ','.star_expression+
-            &&
-            (_opt_var = _PyPegen_expect_token(p, 12), 1)  // ','?
-        )
-        {
-            D(fprintf(stderr, "%*c+ expressions_list[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.star_expression+ ','?"));
-            _res = a;
-            if (_res == NULL && PyErr_Occurred()) {
-                p->error_indicator = 1;
-                D(p->level--);
-                return NULL;
-            }
-            goto done;
-        }
-        p->mark = _mark;
-        D(fprintf(stderr, "%*c%s expressions_list[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','.star_expression+ ','?"));
-    }
-    _res = NULL;
-  done:
-    D(p->level--);
-    return _res;
-}
-
 // star_expressions:
 //     | star_expression ((',' star_expression))+ ','?
 //     | star_expression ','
@@ -6407,7 +6363,7 @@ star_expressions_rule(Parser *p)
         if (
             (a = star_expression_rule(p))  // star_expression
             &&
-            (b = _loop1_72_rule(p))  // ((',' star_expression))+
+            (b = _loop1_70_rule(p))  // ((',' star_expression))+
             &&
             (_opt_var = _PyPegen_expect_token(p, 12), 1)  // ','?
         )
@@ -6602,7 +6558,7 @@ star_named_expressions_rule(Parser *p)
         UNUSED(_opt_var); // Silence compiler warnings
         asdl_seq * a;
         if (
-            (a = _gather_73_rule(p))  // ','.star_named_expression+
+            (a = _gather_71_rule(p))  // ','.star_named_expression+
             &&
             (_opt_var = _PyPegen_expect_token(p, 12), 1)  // ','?
         )
@@ -6794,7 +6750,7 @@ named_expression_rule(Parser *p)
         D(fprintf(stderr, "%*c%s named_expression[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression !':='"));
     }
-    { // invalid_named_expression
+    if (p->call_invalid_rules) { // invalid_named_expression
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
@@ -6907,7 +6863,7 @@ expressions_rule(Parser *p)
         if (
             (a = expression_rule(p))  // expression
             &&
-            (b = _loop1_75_rule(p))  // ((',' expression))+
+            (b = _loop1_73_rule(p))  // ((',' expression))+
             &&
             (_opt_var = _PyPegen_expect_token(p, 12), 1)  // ','?
         )
@@ -7188,7 +7144,7 @@ lambda_params_rule(Parser *p)
     }
     arguments_ty _res = NULL;
     int _mark = p->mark;
-    { // invalid_lambda_parameters
+    if (p->call_invalid_rules) { // invalid_lambda_parameters
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
@@ -7261,9 +7217,9 @@ lambda_parameters_rule(Parser *p)
         if (
             (a = lambda_slash_no_default_rule(p))  // lambda_slash_no_default
             &&
-            (b = _loop0_76_rule(p))  // lambda_param_no_default*
+            (b = _loop0_74_rule(p))  // lambda_param_no_default*
             &&
-            (c = _loop0_77_rule(p))  // lambda_param_with_default*
+            (c = _loop0_75_rule(p))  // lambda_param_with_default*
             &&
             (d = lambda_star_etc_rule(p), 1)  // lambda_star_etc?
         )
@@ -7293,7 +7249,7 @@ lambda_parameters_rule(Parser *p)
         if (
             (a = lambda_slash_with_default_rule(p))  // lambda_slash_with_default
             &&
-            (b = _loop0_78_rule(p))  // lambda_param_with_default*
+            (b = _loop0_76_rule(p))  // lambda_param_with_default*
             &&
             (c = lambda_star_etc_rule(p), 1)  // lambda_star_etc?
         )
@@ -7321,9 +7277,9 @@ lambda_parameters_rule(Parser *p)
         asdl_seq * b;
         void *c;
         if (
-            (a = _loop1_79_rule(p))  // lambda_param_no_default+
+            (a = _loop1_77_rule(p))  // lambda_param_no_default+
             &&
-            (b = _loop0_80_rule(p))  // lambda_param_with_default*
+            (b = _loop0_78_rule(p))  // lambda_param_with_default*
             &&
             (c = lambda_star_etc_rule(p), 1)  // lambda_star_etc?
         )
@@ -7350,7 +7306,7 @@ lambda_parameters_rule(Parser *p)
         asdl_seq * a;
         void *b;
         if (
-            (a = _loop1_81_rule(p))  // lambda_param_with_default+
+            (a = _loop1_79_rule(p))  // lambda_param_with_default+
             &&
             (b = lambda_star_etc_rule(p), 1)  // lambda_star_etc?
         )
@@ -7421,7 +7377,7 @@ lambda_slash_no_default_rule(Parser *p)
         Token * _literal_1;
         asdl_seq * a;
         if (
-            (a = _loop1_82_rule(p))  // lambda_param_no_default+
+            (a = _loop1_80_rule(p))  // lambda_param_no_default+
             &&
             (_literal = _PyPegen_expect_token(p, 17))  // token='/'
             &&
@@ -7450,7 +7406,7 @@ lambda_slash_no_default_rule(Parser *p)
         Token * _literal;
         asdl_seq * a;
         if (
-            (a = _loop1_83_rule(p))  // lambda_param_no_default+
+            (a = _loop1_81_rule(p))  // lambda_param_no_default+
             &&
             (_literal = _PyPegen_expect_token(p, 17))  // token='/'
             &&
@@ -7500,9 +7456,9 @@ lambda_slash_with_default_rule(Parser *p)
         asdl_seq * a;
         asdl_seq * b;
         if (
-            (a = _loop0_84_rule(p))  // lambda_param_no_default*
+            (a = _loop0_82_rule(p))  // lambda_param_no_default*
             &&
-            (b = _loop1_85_rule(p))  // lambda_param_with_default+
+            (b = _loop1_83_rule(p))  // lambda_param_with_default+
             &&
             (_literal = _PyPegen_expect_token(p, 17))  // token='/'
             &&
@@ -7532,9 +7488,9 @@ lambda_slash_with_default_rule(Parser *p)
         asdl_seq * a;
         asdl_seq * b;
         if (
-            (a = _loop0_86_rule(p))  // lambda_param_no_default*
+            (a = _loop0_84_rule(p))  // lambda_param_no_default*
             &&
-            (b = _loop1_87_rule(p))  // lambda_param_with_default+
+            (b = _loop1_85_rule(p))  // lambda_param_with_default+
             &&
             (_literal = _PyPegen_expect_token(p, 17))  // token='/'
             &&
@@ -7590,7 +7546,7 @@ lambda_star_etc_rule(Parser *p)
             &&
             (a = lambda_param_no_default_rule(p))  // lambda_param_no_default
             &&
-            (b = _loop0_88_rule(p))  // lambda_param_maybe_default*
+            (b = _loop0_86_rule(p))  // lambda_param_maybe_default*
             &&
             (c = lambda_kwds_rule(p), 1)  // lambda_kwds?
         )
@@ -7623,7 +7579,7 @@ lambda_star_etc_rule(Parser *p)
             &&
             (_literal_1 = _PyPegen_expect_token(p, 12))  // token=','
             &&
-            (b = _loop1_89_rule(p))  // lambda_param_maybe_default+
+            (b = _loop1_87_rule(p))  // lambda_param_maybe_default+
             &&
             (c = lambda_kwds_rule(p), 1)  // lambda_kwds?
         )
@@ -7665,7 +7621,7 @@ lambda_star_etc_rule(Parser *p)
         D(fprintf(stderr, "%*c%s lambda_star_etc[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_kwds"));
     }
-    { // invalid_lambda_star_etc
+    if (p->call_invalid_rules) { // invalid_lambda_star_etc
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
@@ -8050,7 +8006,7 @@ disjunction_rule(Parser *p)
         if (
             (a = conjunction_rule(p))  // conjunction
             &&
-            (b = _loop1_90_rule(p))  // (('or' conjunction))+
+            (b = _loop1_88_rule(p))  // (('or' conjunction))+
         )
         {
             D(fprintf(stderr, "%*c+ disjunction[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "conjunction (('or' conjunction))+"));
@@ -8136,7 +8092,7 @@ conjunction_rule(Parser *p)
         if (
             (a = inversion_rule(p))  // inversion
             &&
-            (b = _loop1_91_rule(p))  // (('and' inversion))+
+            (b = _loop1_89_rule(p))  // (('and' inversion))+
         )
         {
             D(fprintf(stderr, "%*c+ conjunction[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "inversion (('and' inversion))+"));
@@ -8304,7 +8260,7 @@ comparison_rule(Parser *p)
         if (
             (a = bitwise_or_rule(p))  // bitwise_or
             &&
-            (b = _loop1_92_rule(p))  // compare_op_bitwise_or_pair+
+            (b = _loop1_90_rule(p))  // compare_op_bitwise_or_pair+
         )
         {
             D(fprintf(stderr, "%*c+ comparison[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "bitwise_or compare_op_bitwise_or_pair+"));
@@ -8632,10 +8588,10 @@ noteq_bitwise_or_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> noteq_bitwise_or[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('!=') bitwise_or"));
-        void *_tmp_93_var;
+        void *_tmp_91_var;
         expr_ty a;
         if (
-            (_tmp_93_var = _tmp_93_rule(p))  // '!='
+            (_tmp_91_var = _tmp_91_rule(p))  // '!='
             &&
             (a = bitwise_or_rule(p))  // bitwise_or
         )
@@ -9039,6 +8995,8 @@ bitwise_or_rule(Parser *p)
         }
         p->mark = _mark;
         void *_raw = bitwise_or_raw(p);
+        if (p->error_indicator)
+            return NULL;
         if (_raw == NULL || p->mark <= _resmark)
             break;
         _resmark = p->mark;
@@ -9153,6 +9111,8 @@ bitwise_xor_rule(Parser *p)
         }
         p->mark = _mark;
         void *_raw = bitwise_xor_raw(p);
+        if (p->error_indicator)
+            return NULL;
         if (_raw == NULL || p->mark <= _resmark)
             break;
         _resmark = p->mark;
@@ -9267,6 +9227,8 @@ bitwise_and_rule(Parser *p)
         }
         p->mark = _mark;
         void *_raw = bitwise_and_raw(p);
+        if (p->error_indicator)
+            return NULL;
         if (_raw == NULL || p->mark <= _resmark)
             break;
         _resmark = p->mark;
@@ -9381,6 +9343,8 @@ shift_expr_rule(Parser *p)
         }
         p->mark = _mark;
         void *_raw = shift_expr_raw(p);
+        if (p->error_indicator)
+            return NULL;
         if (_raw == NULL || p->mark <= _resmark)
             break;
         _resmark = p->mark;
@@ -9534,6 +9498,8 @@ sum_rule(Parser *p)
         }
         p->mark = _mark;
         void *_raw = sum_raw(p);
+        if (p->error_indicator)
+            return NULL;
         if (_raw == NULL || p->mark <= _resmark)
             break;
         _resmark = p->mark;
@@ -9693,6 +9659,8 @@ term_rule(Parser *p)
         }
         p->mark = _mark;
         void *_raw = term_raw(p);
+        if (p->error_indicator)
+            return NULL;
         if (_raw == NULL || p->mark <= _resmark)
             break;
         _resmark = p->mark;
@@ -10271,6 +10239,7 @@ await_primary_rule(Parser *p)
 
 // Left-recursive
 // primary:
+//     | invalid_primary
 //     | primary '.' NAME
 //     | primary genexp
 //     | primary '(' arguments? ')'
@@ -10296,6 +10265,8 @@ primary_rule(Parser *p)
         }
         p->mark = _mark;
         void *_raw = primary_raw(p);
+        if (p->error_indicator)
+            return NULL;
         if (_raw == NULL || p->mark <= _resmark)
             break;
         _resmark = p->mark;
@@ -10324,6 +10295,25 @@ primary_raw(Parser *p)
     UNUSED(_start_lineno); // Only used by EXTRA macro
     int _start_col_offset = p->tokens[_mark]->col_offset;
     UNUSED(_start_col_offset); // Only used by EXTRA macro
+    if (p->call_invalid_rules) { // invalid_primary
+        if (p->error_indicator) {
+            D(p->level--);
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> primary[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_primary"));
+        void *invalid_primary_var;
+        if (
+            (invalid_primary_var = invalid_primary_rule(p))  // invalid_primary
+        )
+        {
+            D(fprintf(stderr, "%*c+ primary[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_primary"));
+            _res = invalid_primary_var;
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s primary[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_primary"));
+    }
     { // primary '.' NAME
         if (p->error_indicator) {
             D(p->level--);
@@ -10564,7 +10554,7 @@ slices_rule(Parser *p)
         UNUSED(_opt_var); // Silence compiler warnings
         asdl_seq * a;
         if (
-            (a = _gather_94_rule(p))  // ','.slice+
+            (a = _gather_92_rule(p))  // ','.slice+
             &&
             (_opt_var = _PyPegen_expect_token(p, 12), 1)  // ','?
         )
@@ -10634,7 +10624,7 @@ slice_rule(Parser *p)
             &&
             (b = expression_rule(p), 1)  // expression?
             &&
-            (c = _tmp_96_rule(p), 1)  // [':' expression?]
+            (c = _tmp_94_rule(p), 1)  // [':' expression?]
         )
         {
             D(fprintf(stderr, "%*c+ slice[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression? ':' expression? [':' expression?]"));
@@ -10908,15 +10898,15 @@ atom_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'(' (tuple | group | genexp)"));
-        void *_tmp_97_var;
+        void *_tmp_95_var;
         if (
             _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 7)  // token='('
             &&
-            (_tmp_97_var = _tmp_97_rule(p))  // tuple | group | genexp
+            (_tmp_95_var = _tmp_95_rule(p))  // tuple | group | genexp
         )
         {
             D(fprintf(stderr, "%*c+ atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "&'(' (tuple | group | genexp)"));
-            _res = _tmp_97_var;
+            _res = _tmp_95_var;
             goto done;
         }
         p->mark = _mark;
@@ -10929,15 +10919,15 @@ atom_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'[' (list | listcomp)"));
-        void *_tmp_98_var;
+        void *_tmp_96_var;
         if (
             _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 9)  // token='['
             &&
-            (_tmp_98_var = _tmp_98_rule(p))  // list | listcomp
+            (_tmp_96_var = _tmp_96_rule(p))  // list | listcomp
         )
         {
             D(fprintf(stderr, "%*c+ atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "&'[' (list | listcomp)"));
-            _res = _tmp_98_var;
+            _res = _tmp_96_var;
             goto done;
         }
         p->mark = _mark;
@@ -10950,15 +10940,15 @@ atom_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'{' (dict | set | dictcomp | setcomp)"));
-        void *_tmp_99_var;
+        void *_tmp_97_var;
         if (
             _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 25)  // token='{'
             &&
-            (_tmp_99_var = _tmp_99_rule(p))  // dict | set | dictcomp | setcomp
+            (_tmp_97_var = _tmp_97_rule(p))  // dict | set | dictcomp | setcomp
         )
         {
             D(fprintf(stderr, "%*c+ atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "&'{' (dict | set | dictcomp | setcomp)"));
-            _res = _tmp_99_var;
+            _res = _tmp_97_var;
             goto done;
         }
         p->mark = _mark;
@@ -11027,7 +11017,7 @@ strings_rule(Parser *p)
         D(fprintf(stderr, "%*c> strings[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "STRING+"));
         asdl_seq * a;
         if (
-            (a = _loop1_100_rule(p))  // STRING+
+            (a = _loop1_98_rule(p))  // STRING+
         )
         {
             D(fprintf(stderr, "%*c+ strings[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "STRING+"));
@@ -11184,7 +11174,7 @@ listcomp_rule(Parser *p)
             return NULL;
         }
     }
-    { // invalid_comprehension
+    if (p->call_invalid_rules) { // invalid_comprehension
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
@@ -11241,7 +11231,7 @@ tuple_rule(Parser *p)
         if (
             (_literal = _PyPegen_expect_token(p, 7))  // token='('
             &&
-            (a = _tmp_101_rule(p), 1)  // [star_named_expression ',' star_named_expressions?]
+            (a = _tmp_99_rule(p), 1)  // [star_named_expression ',' star_named_expressions?]
             &&
             (_literal_1 = _PyPegen_expect_token(p, 8))  // token=')'
         )
@@ -11297,7 +11287,7 @@ group_rule(Parser *p)
         if (
             (_literal = _PyPegen_expect_token(p, 7))  // token='('
             &&
-            (a = _tmp_102_rule(p))  // yield_expr | named_expression
+            (a = _tmp_100_rule(p))  // yield_expr | named_expression
             &&
             (_literal_1 = _PyPegen_expect_token(p, 8))  // token=')'
         )
@@ -11315,7 +11305,7 @@ group_rule(Parser *p)
         D(fprintf(stderr, "%*c%s group[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'(' (yield_expr | named_expression) ')'"));
     }
-    { // invalid_group
+    if (p->call_invalid_rules) { // invalid_group
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
@@ -11340,7 +11330,7 @@ group_rule(Parser *p)
     return _res;
 }
 
-// genexp: '(' expression ~ for_if_clauses ')' | invalid_comprehension
+// genexp: '(' named_expression ~ for_if_clauses ')' | invalid_comprehension
 static expr_ty
 genexp_rule(Parser *p)
 {
@@ -11360,12 +11350,12 @@ genexp_rule(Parser *p)
     UNUSED(_start_lineno); // Only used by EXTRA macro
     int _start_col_offset = p->tokens[_mark]->col_offset;
     UNUSED(_start_col_offset); // Only used by EXTRA macro
-    { // '(' expression ~ for_if_clauses ')'
+    { // '(' named_expression ~ for_if_clauses ')'
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> genexp[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'(' expression ~ for_if_clauses ')'"));
+        D(fprintf(stderr, "%*c> genexp[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'(' named_expression ~ for_if_clauses ')'"));
         int _cut_var = 0;
         Token * _literal;
         Token * _literal_1;
@@ -11374,7 +11364,7 @@ genexp_rule(Parser *p)
         if (
             (_literal = _PyPegen_expect_token(p, 7))  // token='('
             &&
-            (a = expression_rule(p))  // expression
+            (a = named_expression_rule(p))  // named_expression
             &&
             (_cut_var = 1)
             &&
@@ -11383,7 +11373,7 @@ genexp_rule(Parser *p)
             (_literal_1 = _PyPegen_expect_token(p, 8))  // token=')'
         )
         {
-            D(fprintf(stderr, "%*c+ genexp[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' expression ~ for_if_clauses ')'"));
+            D(fprintf(stderr, "%*c+ genexp[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' named_expression ~ for_if_clauses ')'"));
             Token *_token = _PyPegen_get_last_nonnwhitespace_token(p);
             if (_token == NULL) {
                 D(p->level--);
@@ -11403,13 +11393,13 @@ genexp_rule(Parser *p)
         }
         p->mark = _mark;
         D(fprintf(stderr, "%*c%s genexp[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'(' expression ~ for_if_clauses ')'"));
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'(' named_expression ~ for_if_clauses ')'"));
         if (_cut_var) {
             D(p->level--);
             return NULL;
         }
     }
-    { // invalid_comprehension
+    if (p->call_invalid_rules) { // invalid_comprehension
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
@@ -11434,7 +11424,7 @@ genexp_rule(Parser *p)
     return _res;
 }
 
-// set: '{' expressions_list '}'
+// set: '{' star_named_expressions '}'
 static expr_ty
 set_rule(Parser *p)
 {
@@ -11454,24 +11444,24 @@ set_rule(Parser *p)
     UNUSED(_start_lineno); // Only used by EXTRA macro
     int _start_col_offset = p->tokens[_mark]->col_offset;
     UNUSED(_start_col_offset); // Only used by EXTRA macro
-    { // '{' expressions_list '}'
+    { // '{' star_named_expressions '}'
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> set[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' expressions_list '}'"));
+        D(fprintf(stderr, "%*c> set[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' star_named_expressions '}'"));
         Token * _literal;
         Token * _literal_1;
         asdl_seq* a;
         if (
             (_literal = _PyPegen_expect_token(p, 25))  // token='{'
             &&
-            (a = expressions_list_rule(p))  // expressions_list
+            (a = star_named_expressions_rule(p))  // star_named_expressions
             &&
             (_literal_1 = _PyPegen_expect_token(p, 26))  // token='}'
         )
         {
-            D(fprintf(stderr, "%*c+ set[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' expressions_list '}'"));
+            D(fprintf(stderr, "%*c+ set[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' star_named_expressions '}'"));
             Token *_token = _PyPegen_get_last_nonnwhitespace_token(p);
             if (_token == NULL) {
                 D(p->level--);
@@ -11491,7 +11481,7 @@ set_rule(Parser *p)
         }
         p->mark = _mark;
         D(fprintf(stderr, "%*c%s set[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' expressions_list '}'"));
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' star_named_expressions '}'"));
     }
     _res = NULL;
   done:
@@ -11499,7 +11489,7 @@ set_rule(Parser *p)
     return _res;
 }
 
-// setcomp: '{' expression ~ for_if_clauses '}' | invalid_comprehension
+// setcomp: '{' named_expression ~ for_if_clauses '}' | invalid_comprehension
 static expr_ty
 setcomp_rule(Parser *p)
 {
@@ -11519,12 +11509,12 @@ setcomp_rule(Parser *p)
     UNUSED(_start_lineno); // Only used by EXTRA macro
     int _start_col_offset = p->tokens[_mark]->col_offset;
     UNUSED(_start_col_offset); // Only used by EXTRA macro
-    { // '{' expression ~ for_if_clauses '}'
+    { // '{' named_expression ~ for_if_clauses '}'
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> setcomp[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' expression ~ for_if_clauses '}'"));
+        D(fprintf(stderr, "%*c> setcomp[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' named_expression ~ for_if_clauses '}'"));
         int _cut_var = 0;
         Token * _literal;
         Token * _literal_1;
@@ -11533,7 +11523,7 @@ setcomp_rule(Parser *p)
         if (
             (_literal = _PyPegen_expect_token(p, 25))  // token='{'
             &&
-            (a = expression_rule(p))  // expression
+            (a = named_expression_rule(p))  // named_expression
             &&
             (_cut_var = 1)
             &&
@@ -11542,7 +11532,7 @@ setcomp_rule(Parser *p)
             (_literal_1 = _PyPegen_expect_token(p, 26))  // token='}'
         )
         {
-            D(fprintf(stderr, "%*c+ setcomp[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' expression ~ for_if_clauses '}'"));
+            D(fprintf(stderr, "%*c+ setcomp[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' named_expression ~ for_if_clauses '}'"));
             Token *_token = _PyPegen_get_last_nonnwhitespace_token(p);
             if (_token == NULL) {
                 D(p->level--);
@@ -11562,13 +11552,13 @@ setcomp_rule(Parser *p)
         }
         p->mark = _mark;
         D(fprintf(stderr, "%*c%s setcomp[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' expression ~ for_if_clauses '}'"));
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' named_expression ~ for_if_clauses '}'"));
         if (_cut_var) {
             D(p->level--);
             return NULL;
         }
     }
-    { // invalid_comprehension
+    if (p->call_invalid_rules) { // invalid_comprehension
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
@@ -11720,7 +11710,7 @@ dictcomp_rule(Parser *p)
         D(fprintf(stderr, "%*c%s dictcomp[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' kvpair for_if_clauses '}'"));
     }
-    { // invalid_dict_comprehension
+    if (p->call_invalid_rules) { // invalid_dict_comprehension
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
@@ -11766,7 +11756,7 @@ double_starred_kvpairs_rule(Parser *p)
         UNUSED(_opt_var); // Silence compiler warnings
         asdl_seq * a;
         if (
-            (a = _gather_103_rule(p))  // ','.double_starred_kvpair+
+            (a = _gather_101_rule(p))  // ','.double_starred_kvpair+
             &&
             (_opt_var = _PyPegen_expect_token(p, 12), 1)  // ','?
         )
@@ -11917,13 +11907,13 @@ for_if_clauses_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> for_if_clauses[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "for_if_clause+"));
-        asdl_seq * _loop1_105_var;
+        asdl_seq * _loop1_103_var;
         if (
-            (_loop1_105_var = _loop1_105_rule(p))  // for_if_clause+
+            (_loop1_103_var = _loop1_103_rule(p))  // for_if_clause+
         )
         {
             D(fprintf(stderr, "%*c+ for_if_clauses[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "for_if_clause+"));
-            _res = _loop1_105_var;
+            _res = _loop1_103_var;
             goto done;
         }
         p->mark = _mark;
@@ -11976,7 +11966,7 @@ for_if_clause_rule(Parser *p)
             &&
             (b = disjunction_rule(p))  // disjunction
             &&
-            (c = _loop0_106_rule(p))  // (('if' disjunction))*
+            (c = _loop0_104_rule(p))  // (('if' disjunction))*
         )
         {
             D(fprintf(stderr, "%*c+ for_if_clause[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "ASYNC 'for' star_targets 'in' ~ disjunction (('if' disjunction))*"));
@@ -12019,7 +12009,7 @@ for_if_clause_rule(Parser *p)
             &&
             (b = disjunction_rule(p))  // disjunction
             &&
-            (c = _loop0_107_rule(p))  // (('if' disjunction))*
+            (c = _loop0_105_rule(p))  // (('if' disjunction))*
         )
         {
             D(fprintf(stderr, "%*c+ for_if_clause[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'for' star_targets 'in' ~ disjunction (('if' disjunction))*"));
@@ -12039,7 +12029,7 @@ for_if_clause_rule(Parser *p)
             return NULL;
         }
     }
-    { // invalid_for_target
+    if (p->call_invalid_rules) { // invalid_for_target
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
@@ -12165,7 +12155,7 @@ yield_expr_rule(Parser *p)
     return _res;
 }
 
-// arguments: args ','? &')' | incorrect_arguments
+// arguments: args ','? &')' | invalid_arguments
 static expr_ty
 arguments_rule(Parser *p)
 {
@@ -12210,24 +12200,24 @@ arguments_rule(Parser *p)
         D(fprintf(stderr, "%*c%s arguments[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args ','? &')'"));
     }
-    { // incorrect_arguments
+    if (p->call_invalid_rules) { // invalid_arguments
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "incorrect_arguments"));
-        void *incorrect_arguments_var;
+        D(fprintf(stderr, "%*c> arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_arguments"));
+        void *invalid_arguments_var;
         if (
-            (incorrect_arguments_var = incorrect_arguments_rule(p))  // incorrect_arguments
+            (invalid_arguments_var = invalid_arguments_rule(p))  // invalid_arguments
         )
         {
-            D(fprintf(stderr, "%*c+ arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "incorrect_arguments"));
-            _res = incorrect_arguments_var;
+            D(fprintf(stderr, "%*c+ arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_arguments"));
+            _res = invalid_arguments_var;
             goto done;
         }
         p->mark = _mark;
         D(fprintf(stderr, "%*c%s arguments[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "incorrect_arguments"));
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_arguments"));
     }
     _res = NULL;
   done:
@@ -12265,9 +12255,9 @@ args_rule(Parser *p)
         asdl_seq * a;
         void *b;
         if (
-            (a = _gather_108_rule(p))  // ','.(starred_expression | named_expression !'=')+
+            (a = _gather_106_rule(p))  // ','.(starred_expression | named_expression !'=')+
             &&
-            (b = _tmp_110_rule(p), 1)  // [',' kwargs]
+            (b = _tmp_108_rule(p), 1)  // [',' kwargs]
         )
         {
             D(fprintf(stderr, "%*c+ args[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | named_expression !'=')+ [',' kwargs]"));
@@ -12355,11 +12345,11 @@ kwargs_rule(Parser *p)
         asdl_seq * a;
         asdl_seq * b;
         if (
-            (a = _gather_111_rule(p))  // ','.kwarg_or_starred+
+            (a = _gather_109_rule(p))  // ','.kwarg_or_starred+
             &&
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
             &&
-            (b = _gather_113_rule(p))  // ','.kwarg_or_double_starred+
+            (b = _gather_111_rule(p))  // ','.kwarg_or_double_starred+
         )
         {
             D(fprintf(stderr, "%*c+ kwargs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.kwarg_or_starred+ ',' ','.kwarg_or_double_starred+"));
@@ -12381,13 +12371,13 @@ kwargs_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> kwargs[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.kwarg_or_starred+"));
-        asdl_seq * _gather_115_var;
+        asdl_seq * _gather_113_var;
         if (
-            (_gather_115_var = _gather_115_rule(p))  // ','.kwarg_or_starred+
+            (_gather_113_var = _gather_113_rule(p))  // ','.kwarg_or_starred+
         )
         {
             D(fprintf(stderr, "%*c+ kwargs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.kwarg_or_starred+"));
-            _res = _gather_115_var;
+            _res = _gather_113_var;
             goto done;
         }
         p->mark = _mark;
@@ -12400,13 +12390,13 @@ kwargs_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> kwargs[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.kwarg_or_double_starred+"));
-        asdl_seq * _gather_117_var;
+        asdl_seq * _gather_115_var;
         if (
-            (_gather_117_var = _gather_117_rule(p))  // ','.kwarg_or_double_starred+
+            (_gather_115_var = _gather_115_rule(p))  // ','.kwarg_or_double_starred+
         )
         {
             D(fprintf(stderr, "%*c+ kwargs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.kwarg_or_double_starred+"));
-            _res = _gather_117_var;
+            _res = _gather_115_var;
             goto done;
         }
         p->mark = _mark;
@@ -12564,7 +12554,7 @@ kwarg_or_starred_rule(Parser *p)
         D(fprintf(stderr, "%*c%s kwarg_or_starred[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression"));
     }
-    { // invalid_kwarg
+    if (p->call_invalid_rules) { // invalid_kwarg
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
@@ -12684,7 +12674,7 @@ kwarg_or_double_starred_rule(Parser *p)
         D(fprintf(stderr, "%*c%s kwarg_or_double_starred[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**' expression"));
     }
-    { // invalid_kwarg
+    if (p->call_invalid_rules) { // invalid_kwarg
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
@@ -12768,7 +12758,7 @@ star_targets_rule(Parser *p)
         if (
             (a = star_target_rule(p))  // star_target
             &&
-            (b = _loop0_119_rule(p))  // ((',' star_target))*
+            (b = _loop0_117_rule(p))  // ((',' star_target))*
             &&
             (_opt_var = _PyPegen_expect_token(p, 12), 1)  // ','?
         )
@@ -12822,7 +12812,7 @@ star_targets_seq_rule(Parser *p)
         UNUSED(_opt_var); // Silence compiler warnings
         asdl_seq * a;
         if (
-            (a = _gather_120_rule(p))  // ','.star_target+
+            (a = _gather_118_rule(p))  // ','.star_target+
             &&
             (_opt_var = _PyPegen_expect_token(p, 12), 1)  // ','?
         )
@@ -12885,7 +12875,7 @@ star_target_rule(Parser *p)
         if (
             (_literal = _PyPegen_expect_token(p, 16))  // token='*'
             &&
-            (a = _tmp_122_rule(p))  // !'*' star_target
+            (a = _tmp_120_rule(p))  // !'*' star_target
         )
         {
             D(fprintf(stderr, "%*c+ star_target[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (!'*' star_target)"));
@@ -13407,7 +13397,7 @@ del_targets_rule(Parser *p)
         UNUSED(_opt_var); // Silence compiler warnings
         asdl_seq * a;
         if (
-            (a = _gather_123_rule(p))  // ','.del_target+
+            (a = _gather_121_rule(p))  // ','.del_target+
             &&
             (_opt_var = _PyPegen_expect_token(p, 12), 1)  // ','?
         )
@@ -13748,7 +13738,7 @@ targets_rule(Parser *p)
         UNUSED(_opt_var); // Silence compiler warnings
         asdl_seq * a;
         if (
-            (a = _gather_125_rule(p))  // ','.target+
+            (a = _gather_123_rule(p))  // ','.target+
             &&
             (_opt_var = _PyPegen_expect_token(p, 12), 1)  // ','?
         )
@@ -13937,6 +13927,8 @@ t_primary_rule(Parser *p)
         }
         p->mark = _mark;
         void *_raw = t_primary_raw(p);
+        if (p->error_indicator)
+            return NULL;
         if (_raw == NULL || p->mark <= _resmark)
             break;
         _resmark = p->mark;
@@ -14396,14 +14388,14 @@ t_atom_rule(Parser *p)
     return _res;
 }
 
-// incorrect_arguments:
+// invalid_arguments:
 //     | args ',' '*'
 //     | expression for_if_clauses ',' [args | expression for_if_clauses]
 //     | args for_if_clauses
 //     | args ',' expression for_if_clauses
 //     | args ',' args
 static void *
-incorrect_arguments_rule(Parser *p)
+invalid_arguments_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -14417,7 +14409,7 @@ incorrect_arguments_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> incorrect_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ',' '*'"));
+        D(fprintf(stderr, "%*c> invalid_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ',' '*'"));
         Token * _literal;
         Token * _literal_1;
         expr_ty args_var;
@@ -14429,7 +14421,7 @@ incorrect_arguments_rule(Parser *p)
             (_literal_1 = _PyPegen_expect_token(p, 16))  // token='*'
         )
         {
-            D(fprintf(stderr, "%*c+ incorrect_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ',' '*'"));
+            D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ',' '*'"));
             _res = RAISE_SYNTAX_ERROR ( "iterable argument unpacking follows keyword argument unpacking" );
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -14439,7 +14431,7 @@ incorrect_arguments_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s incorrect_arguments[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s invalid_arguments[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args ',' '*'"));
     }
     { // expression for_if_clauses ',' [args | expression for_if_clauses]
@@ -14447,7 +14439,7 @@ incorrect_arguments_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> incorrect_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses ',' [args | expression for_if_clauses]"));
+        D(fprintf(stderr, "%*c> invalid_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses ',' [args | expression for_if_clauses]"));
         Token * _literal;
         void *_opt_var;
         UNUSED(_opt_var); // Silence compiler warnings
@@ -14460,10 +14452,10 @@ incorrect_arguments_rule(Parser *p)
             &&
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
             &&
-            (_opt_var = _tmp_127_rule(p), 1)  // [args | expression for_if_clauses]
+            (_opt_var = _tmp_125_rule(p), 1)  // [args | expression for_if_clauses]
         )
         {
-            D(fprintf(stderr, "%*c+ incorrect_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses ',' [args | expression for_if_clauses]"));
+            D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses ',' [args | expression for_if_clauses]"));
             _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "Generator expression must be parenthesized" );
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -14473,7 +14465,7 @@ incorrect_arguments_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s incorrect_arguments[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s invalid_arguments[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression for_if_clauses ',' [args | expression for_if_clauses]"));
     }
     { // args for_if_clauses
@@ -14481,7 +14473,7 @@ incorrect_arguments_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> incorrect_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args for_if_clauses"));
+        D(fprintf(stderr, "%*c> invalid_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args for_if_clauses"));
         expr_ty a;
         asdl_seq* for_if_clauses_var;
         if (
@@ -14490,7 +14482,7 @@ incorrect_arguments_rule(Parser *p)
             (for_if_clauses_var = for_if_clauses_rule(p))  // for_if_clauses
         )
         {
-            D(fprintf(stderr, "%*c+ incorrect_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args for_if_clauses"));
+            D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args for_if_clauses"));
             _res = _PyPegen_nonparen_genexp_in_call ( p , a );
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -14500,7 +14492,7 @@ incorrect_arguments_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s incorrect_arguments[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s invalid_arguments[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args for_if_clauses"));
     }
     { // args ',' expression for_if_clauses
@@ -14508,7 +14500,7 @@ incorrect_arguments_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> incorrect_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ',' expression for_if_clauses"));
+        D(fprintf(stderr, "%*c> invalid_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ',' expression for_if_clauses"));
         Token * _literal;
         expr_ty a;
         expr_ty args_var;
@@ -14523,7 +14515,7 @@ incorrect_arguments_rule(Parser *p)
             (for_if_clauses_var = for_if_clauses_rule(p))  // for_if_clauses
         )
         {
-            D(fprintf(stderr, "%*c+ incorrect_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ',' expression for_if_clauses"));
+            D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ',' expression for_if_clauses"));
             _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "Generator expression must be parenthesized" );
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -14533,7 +14525,7 @@ incorrect_arguments_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s incorrect_arguments[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s invalid_arguments[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args ',' expression for_if_clauses"));
     }
     { // args ',' args
@@ -14541,7 +14533,7 @@ incorrect_arguments_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> incorrect_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ',' args"));
+        D(fprintf(stderr, "%*c> invalid_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ',' args"));
         Token * _literal;
         expr_ty a;
         expr_ty args_var;
@@ -14553,7 +14545,7 @@ incorrect_arguments_rule(Parser *p)
             (args_var = args_rule(p))  // args
         )
         {
-            D(fprintf(stderr, "%*c+ incorrect_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ',' args"));
+            D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ',' args"));
             _res = _PyPegen_arguments_parsing_error ( p , a );
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -14563,7 +14555,7 @@ incorrect_arguments_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s incorrect_arguments[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s invalid_arguments[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args ',' args"));
     }
     _res = NULL;
@@ -14718,7 +14710,7 @@ invalid_assignment_rule(Parser *p)
         D(fprintf(stderr, "%*c> invalid_assignment[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expression ',' star_named_expressions* ':' expression"));
         Token * _literal;
         Token * _literal_1;
-        asdl_seq * _loop0_128_var;
+        asdl_seq * _loop0_126_var;
         expr_ty a;
         expr_ty expression_var;
         if (
@@ -14726,7 +14718,7 @@ invalid_assignment_rule(Parser *p)
             &&
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
             &&
-            (_loop0_128_var = _loop0_128_rule(p))  // star_named_expressions*
+            (_loop0_126_var = _loop0_126_rule(p))  // star_named_expressions*
             &&
             (_literal_1 = _PyPegen_expect_token(p, 11))  // token=':'
             &&
@@ -14783,10 +14775,10 @@ invalid_assignment_rule(Parser *p)
         }
         D(fprintf(stderr, "%*c> invalid_assignment[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "((star_targets '='))* star_expressions '='"));
         Token * _literal;
-        asdl_seq * _loop0_129_var;
+        asdl_seq * _loop0_127_var;
         expr_ty a;
         if (
-            (_loop0_129_var = _loop0_129_rule(p))  // ((star_targets '='))*
+            (_loop0_127_var = _loop0_127_rule(p))  // ((star_targets '='))*
             &&
             (a = star_expressions_rule(p))  // star_expressions
             &&
@@ -14813,10 +14805,10 @@ invalid_assignment_rule(Parser *p)
         }
         D(fprintf(stderr, "%*c> invalid_assignment[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "((star_targets '='))* yield_expr '='"));
         Token * _literal;
-        asdl_seq * _loop0_130_var;
+        asdl_seq * _loop0_128_var;
         expr_ty a;
         if (
-            (_loop0_130_var = _loop0_130_rule(p))  // ((star_targets '='))*
+            (_loop0_128_var = _loop0_128_rule(p))  // ((star_targets '='))*
             &&
             (a = yield_expr_rule(p))  // yield_expr
             &&
@@ -14842,7 +14834,7 @@ invalid_assignment_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> invalid_assignment[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_expressions augassign (yield_expr | star_expressions)"));
-        void *_tmp_131_var;
+        void *_tmp_129_var;
         expr_ty a;
         AugOperator* augassign_var;
         if (
@@ -14850,7 +14842,7 @@ invalid_assignment_rule(Parser *p)
             &&
             (augassign_var = augassign_rule(p))  // augassign
             &&
-            (_tmp_131_var = _tmp_131_rule(p))  // yield_expr | star_expressions
+            (_tmp_129_var = _tmp_129_rule(p))  // yield_expr | star_expressions
         )
         {
             D(fprintf(stderr, "%*c+ invalid_assignment[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_expressions augassign (yield_expr | star_expressions)"));
@@ -15044,6 +15036,51 @@ invalid_block_rule(Parser *p)
     return _res;
 }
 
+// Left-recursive
+// invalid_primary: primary '{'
+static void *
+invalid_primary_rule(Parser *p)
+{
+    D(p->level++);
+    if (p->error_indicator) {
+        D(p->level--);
+        return NULL;
+    }
+    void * _res = NULL;
+    int _mark = p->mark;
+    { // primary '{'
+        if (p->error_indicator) {
+            D(p->level--);
+            return NULL;
+        }
+        D(fprintf(stderr, "%*c> invalid_primary[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "primary '{'"));
+        Token * a;
+        expr_ty primary_var;
+        if (
+            (primary_var = primary_rule(p))  // primary
+            &&
+            (a = _PyPegen_expect_token(p, 25))  // token='{'
+        )
+        {
+            D(fprintf(stderr, "%*c+ invalid_primary[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "primary '{'"));
+            _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "invalid syntax" );
+            if (_res == NULL && PyErr_Occurred()) {
+                p->error_indicator = 1;
+                D(p->level--);
+                return NULL;
+            }
+            goto done;
+        }
+        p->mark = _mark;
+        D(fprintf(stderr, "%*c%s invalid_primary[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "primary '{'"));
+    }
+    _res = NULL;
+  done:
+    D(p->level--);
+    return _res;
+}
+
 // invalid_comprehension: ('[' | '(' | '{') starred_expression for_if_clauses
 static void *
 invalid_comprehension_rule(Parser *p)
@@ -15061,11 +15098,11 @@ invalid_comprehension_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> invalid_comprehension[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('[' | '(' | '{') starred_expression for_if_clauses"));
-        void *_tmp_132_var;
+        void *_tmp_130_var;
         expr_ty a;
         asdl_seq* for_if_clauses_var;
         if (
-            (_tmp_132_var = _tmp_132_rule(p))  // '[' | '(' | '{'
+            (_tmp_130_var = _tmp_130_rule(p))  // '[' | '(' | '{'
             &&
             (a = starred_expression_rule(p))  // starred_expression
             &&
@@ -15162,13 +15199,13 @@ invalid_parameters_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> invalid_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default* (slash_with_default | param_with_default+) param_no_default"));
-        asdl_seq * _loop0_133_var;
-        void *_tmp_134_var;
+        asdl_seq * _loop0_131_var;
+        void *_tmp_132_var;
         arg_ty param_no_default_var;
         if (
-            (_loop0_133_var = _loop0_133_rule(p))  // param_no_default*
+            (_loop0_131_var = _loop0_131_rule(p))  // param_no_default*
             &&
-            (_tmp_134_var = _tmp_134_rule(p))  // slash_with_default | param_with_default+
+            (_tmp_132_var = _tmp_132_rule(p))  // slash_with_default | param_with_default+
             &&
             (param_no_default_var = param_no_default_rule(p))  // param_no_default
         )
@@ -15210,13 +15247,13 @@ invalid_lambda_parameters_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> invalid_lambda_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default* (lambda_slash_with_default | lambda_param_with_default+) lambda_param_no_default"));
-        asdl_seq * _loop0_135_var;
-        void *_tmp_136_var;
+        asdl_seq * _loop0_133_var;
+        void *_tmp_134_var;
         arg_ty lambda_param_no_default_var;
         if (
-            (_loop0_135_var = _loop0_135_rule(p))  // lambda_param_no_default*
+            (_loop0_133_var = _loop0_133_rule(p))  // lambda_param_no_default*
             &&
-            (_tmp_136_var = _tmp_136_rule(p))  // lambda_slash_with_default | lambda_param_with_default+
+            (_tmp_134_var = _tmp_134_rule(p))  // lambda_slash_with_default | lambda_param_with_default+
             &&
             (lambda_param_no_default_var = lambda_param_no_default_rule(p))  // lambda_param_no_default
         )
@@ -15258,11 +15295,11 @@ invalid_star_etc_rule(Parser *p)
         }
         D(fprintf(stderr, "%*c> invalid_star_etc[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' (')' | ',' (')' | '**'))"));
         Token * _literal;
-        void *_tmp_137_var;
+        void *_tmp_135_var;
         if (
             (_literal = _PyPegen_expect_token(p, 16))  // token='*'
             &&
-            (_tmp_137_var = _tmp_137_rule(p))  // ')' | ',' (')' | '**')
+            (_tmp_135_var = _tmp_135_rule(p))  // ')' | ',' (')' | '**')
         )
         {
             D(fprintf(stderr, "%*c+ invalid_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (')' | ',' (')' | '**'))"));
@@ -15332,11 +15369,11 @@ invalid_lambda_star_etc_rule(Parser *p)
         }
         D(fprintf(stderr, "%*c> invalid_lambda_star_etc[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' (':' | ',' (':' | '**'))"));
         Token * _literal;
-        void *_tmp_138_var;
+        void *_tmp_136_var;
         if (
             (_literal = _PyPegen_expect_token(p, 16))  // token='*'
             &&
-            (_tmp_138_var = _tmp_138_rule(p))  // ':' | ',' (':' | '**')
+            (_tmp_136_var = _tmp_136_rule(p))  // ':' | ',' (':' | '**')
         )
         {
             D(fprintf(stderr, "%*c+ invalid_lambda_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (':' | ',' (':' | '**'))"));
@@ -16845,12 +16882,12 @@ _loop1_22_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> _loop1_22[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')"));
-        void *_tmp_139_var;
+        void *_tmp_137_var;
         while (
-            (_tmp_139_var = _tmp_139_rule(p))  // star_targets '='
+            (_tmp_137_var = _tmp_137_rule(p))  // star_targets '='
         )
         {
-            _res = _tmp_139_var;
+            _res = _tmp_137_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -17353,12 +17390,12 @@ _loop0_31_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> _loop0_31[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('.' | '...')"));
-        void *_tmp_140_var;
+        void *_tmp_138_var;
         while (
-            (_tmp_140_var = _tmp_140_rule(p))  // '.' | '...'
+            (_tmp_138_var = _tmp_138_rule(p))  // '.' | '...'
         )
         {
-            _res = _tmp_140_var;
+            _res = _tmp_138_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -17419,12 +17456,12 @@ _loop1_32_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> _loop1_32[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('.' | '...')"));
-        void *_tmp_141_var;
+        void *_tmp_139_var;
         while (
-            (_tmp_141_var = _tmp_141_rule(p))  // '.' | '...'
+            (_tmp_139_var = _tmp_139_rule(p))  // '.' | '...'
         )
         {
-            _res = _tmp_141_var;
+            _res = _tmp_139_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -19581,12 +19618,12 @@ _loop1_68_rule(Parser *p)
             return NULL;
         }
         D(fprintf(stderr, "%*c> _loop1_68[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('@' named_expression NEWLINE)"));
-        void *_tmp_142_var;
+        void *_tmp_140_var;
         while (
-            (_tmp_142_var = _tmp_142_rule(p))  // '@' named_expression NEWLINE
+            (_tmp_140_var = _tmp_140_rule(p))  // '@' named_expression NEWLINE
         )
         {
-            _res = _tmp_142_var;
+            _res = _tmp_140_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -19672,123 +19709,9 @@ _tmp_69_rule(Parser *p)
     return _res;
 }
 
-// _loop0_71: ',' star_expression
-static asdl_seq *
-_loop0_71_rule(Parser *p)
-{
-    D(p->level++);
-    if (p->error_indicator) {
-        D(p->level--);
-        return NULL;
-    }
-    void *_res = NULL;
-    int _mark = p->mark;
-    int _start_mark = p->mark;
-    void **_children = PyMem_Malloc(sizeof(void *));
-    if (!_children) {
-        p->error_indicator = 1;
-        PyErr_NoMemory();
-        D(p->level--);
-        return NULL;
-    }
-    ssize_t _children_capacity = 1;
-    ssize_t _n = 0;
-    { // ',' star_expression
-        if (p->error_indicator) {
-            D(p->level--);
-            return NULL;
-        }
-        D(fprintf(stderr, "%*c> _loop0_71[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_expression"));
-        Token * _literal;
-        expr_ty elem;
-        while (
-            (_literal = _PyPegen_expect_token(p, 12))  // token=','
-            &&
-            (elem = star_expression_rule(p))  // star_expression
-        )
-        {
-            _res = elem;
-            if (_res == NULL && PyErr_Occurred()) {
-                p->error_indicator = 1;
-                PyMem_Free(_children);
-                D(p->level--);
-                return NULL;
-            }
-            if (_n == _children_capacity) {
-                _children_capacity *= 2;
-                void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
-                if (!_new_children) {
-                    p->error_indicator = 1;
-                    PyErr_NoMemory();
-                    D(p->level--);
-                    return NULL;
-                }
-                _children = _new_children;
-            }
-            _children[_n++] = _res;
-            _mark = p->mark;
-        }
-        p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_71[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_expression"));
-    }
-    asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
-    if (!_seq) {
-        PyMem_Free(_children);
-        p->error_indicator = 1;
-        PyErr_NoMemory();
-        D(p->level--);
-        return NULL;
-    }
-    for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
-    PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_71_type, _seq);
-    D(p->level--);
-    return _seq;
-}
-
-// _gather_70: star_expression _loop0_71
-static asdl_seq *
-_gather_70_rule(Parser *p)
-{
-    D(p->level++);
-    if (p->error_indicator) {
-        D(p->level--);
-        return NULL;
-    }
-    asdl_seq * _res = NULL;
-    int _mark = p->mark;
-    { // star_expression _loop0_71
-        if (p->error_indicator) {
-            D(p->level--);
-            return NULL;
-        }
-        D(fprintf(stderr, "%*c> _gather_70[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_expression _loop0_71"));
-        expr_ty elem;
-        asdl_seq * seq;
-        if (
-            (elem = star_expression_rule(p))  // star_expression
-            &&
-            (seq = _loop0_71_rule(p))  // _loop0_71
-        )
-        {
-            D(fprintf(stderr, "%*c+ _gather_70[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_expression _loop0_71"));
-            _res = _PyPegen_seq_insert_in_front(p, elem, seq);
-            goto done;
-        }
-        p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_70[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_expression _loop0_71"));
-    }
-    _res = NULL;
-  done:
-    D(p->level--);
-    return _res;
-}
-
-// _loop1_72: (',' star_expression)
+// _loop1_70: (',' star_expression)
 static asdl_seq *
-_loop1_72_rule(Parser *p)
+_loop1_70_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -19812,13 +19735,13 @@ _loop1_72_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_72[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_expression)"));
-        void *_tmp_143_var;
+        D(fprintf(stderr, "%*c> _loop1_70[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_expression)"));
+        void *_tmp_141_var;
         while (
-            (_tmp_143_var = _tmp_143_rule(p))  // ',' star_expression
+            (_tmp_141_var = _tmp_141_rule(p))  // ',' star_expression
         )
         {
-            _res = _tmp_143_var;
+            _res = _tmp_141_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -19834,7 +19757,7 @@ _loop1_72_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_72[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_70[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(',' star_expression)"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -19852,14 +19775,14 @@ _loop1_72_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop1_72_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop1_70_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop0_74: ',' star_named_expression
+// _loop0_72: ',' star_named_expression
 static asdl_seq *
-_loop0_74_rule(Parser *p)
+_loop0_72_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -19883,7 +19806,7 @@ _loop0_74_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_74[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_named_expression"));
+        D(fprintf(stderr, "%*c> _loop0_72[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_named_expression"));
         Token * _literal;
         expr_ty elem;
         while (
@@ -19914,7 +19837,7 @@ _loop0_74_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_74[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_72[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_named_expression"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -19927,14 +19850,14 @@ _loop0_74_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_74_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_72_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _gather_73: star_named_expression _loop0_74
+// _gather_71: star_named_expression _loop0_72
 static asdl_seq *
-_gather_73_rule(Parser *p)
+_gather_71_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -19943,27 +19866,27 @@ _gather_73_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // star_named_expression _loop0_74
+    { // star_named_expression _loop0_72
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_73[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expression _loop0_74"));
+        D(fprintf(stderr, "%*c> _gather_71[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expression _loop0_72"));
         expr_ty elem;
         asdl_seq * seq;
         if (
             (elem = star_named_expression_rule(p))  // star_named_expression
             &&
-            (seq = _loop0_74_rule(p))  // _loop0_74
+            (seq = _loop0_72_rule(p))  // _loop0_72
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_73[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_named_expression _loop0_74"));
+            D(fprintf(stderr, "%*c+ _gather_71[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_named_expression _loop0_72"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_73[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_named_expression _loop0_74"));
+        D(fprintf(stderr, "%*c%s _gather_71[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_named_expression _loop0_72"));
     }
     _res = NULL;
   done:
@@ -19971,9 +19894,9 @@ _gather_73_rule(Parser *p)
     return _res;
 }
 
-// _loop1_75: (',' expression)
+// _loop1_73: (',' expression)
 static asdl_seq *
-_loop1_75_rule(Parser *p)
+_loop1_73_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -19997,13 +19920,13 @@ _loop1_75_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_75[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' expression)"));
-        void *_tmp_144_var;
+        D(fprintf(stderr, "%*c> _loop1_73[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' expression)"));
+        void *_tmp_142_var;
         while (
-            (_tmp_144_var = _tmp_144_rule(p))  // ',' expression
+            (_tmp_142_var = _tmp_142_rule(p))  // ',' expression
         )
         {
-            _res = _tmp_144_var;
+            _res = _tmp_142_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -20019,7 +19942,7 @@ _loop1_75_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_75[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_73[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(',' expression)"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -20037,14 +19960,14 @@ _loop1_75_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop1_75_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop1_73_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop0_76: lambda_param_no_default
+// _loop0_74: lambda_param_no_default
 static asdl_seq *
-_loop0_76_rule(Parser *p)
+_loop0_74_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -20068,7 +19991,7 @@ _loop0_76_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_76[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default"));
+        D(fprintf(stderr, "%*c> _loop0_74[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default"));
         arg_ty lambda_param_no_default_var;
         while (
             (lambda_param_no_default_var = lambda_param_no_default_rule(p))  // lambda_param_no_default
@@ -20090,7 +20013,7 @@ _loop0_76_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_76[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_74[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -20103,14 +20026,14 @@ _loop0_76_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_76_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_74_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop0_77: lambda_param_with_default
+// _loop0_75: lambda_param_with_default
 static asdl_seq *
-_loop0_77_rule(Parser *p)
+_loop0_75_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -20134,7 +20057,7 @@ _loop0_77_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_77[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default"));
+        D(fprintf(stderr, "%*c> _loop0_75[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default"));
         NameDefaultPair* lambda_param_with_default_var;
         while (
             (lambda_param_with_default_var = lambda_param_with_default_rule(p))  // lambda_param_with_default
@@ -20156,7 +20079,7 @@ _loop0_77_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_77[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_75[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_with_default"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -20169,14 +20092,14 @@ _loop0_77_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_77_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_75_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop0_78: lambda_param_with_default
+// _loop0_76: lambda_param_with_default
 static asdl_seq *
-_loop0_78_rule(Parser *p)
+_loop0_76_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -20200,7 +20123,7 @@ _loop0_78_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_78[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default"));
+        D(fprintf(stderr, "%*c> _loop0_76[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default"));
         NameDefaultPair* lambda_param_with_default_var;
         while (
             (lambda_param_with_default_var = lambda_param_with_default_rule(p))  // lambda_param_with_default
@@ -20222,7 +20145,7 @@ _loop0_78_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_78[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_76[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_with_default"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -20235,14 +20158,14 @@ _loop0_78_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_78_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_76_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop1_79: lambda_param_no_default
+// _loop1_77: lambda_param_no_default
 static asdl_seq *
-_loop1_79_rule(Parser *p)
+_loop1_77_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -20266,7 +20189,7 @@ _loop1_79_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_79[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default"));
+        D(fprintf(stderr, "%*c> _loop1_77[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default"));
         arg_ty lambda_param_no_default_var;
         while (
             (lambda_param_no_default_var = lambda_param_no_default_rule(p))  // lambda_param_no_default
@@ -20288,7 +20211,7 @@ _loop1_79_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_79[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_77[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -20306,14 +20229,14 @@ _loop1_79_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop1_79_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop1_77_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop0_80: lambda_param_with_default
+// _loop0_78: lambda_param_with_default
 static asdl_seq *
-_loop0_80_rule(Parser *p)
+_loop0_78_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -20337,7 +20260,7 @@ _loop0_80_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_80[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default"));
+        D(fprintf(stderr, "%*c> _loop0_78[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default"));
         NameDefaultPair* lambda_param_with_default_var;
         while (
             (lambda_param_with_default_var = lambda_param_with_default_rule(p))  // lambda_param_with_default
@@ -20359,7 +20282,7 @@ _loop0_80_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_80[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_78[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_with_default"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -20372,14 +20295,14 @@ _loop0_80_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_80_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_78_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop1_81: lambda_param_with_default
+// _loop1_79: lambda_param_with_default
 static asdl_seq *
-_loop1_81_rule(Parser *p)
+_loop1_79_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -20403,7 +20326,7 @@ _loop1_81_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_81[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default"));
+        D(fprintf(stderr, "%*c> _loop1_79[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default"));
         NameDefaultPair* lambda_param_with_default_var;
         while (
             (lambda_param_with_default_var = lambda_param_with_default_rule(p))  // lambda_param_with_default
@@ -20425,7 +20348,7 @@ _loop1_81_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_81[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_79[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_with_default"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -20443,14 +20366,14 @@ _loop1_81_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop1_81_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop1_79_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop1_82: lambda_param_no_default
+// _loop1_80: lambda_param_no_default
 static asdl_seq *
-_loop1_82_rule(Parser *p)
+_loop1_80_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -20474,7 +20397,7 @@ _loop1_82_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_82[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default"));
+        D(fprintf(stderr, "%*c> _loop1_80[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default"));
         arg_ty lambda_param_no_default_var;
         while (
             (lambda_param_no_default_var = lambda_param_no_default_rule(p))  // lambda_param_no_default
@@ -20496,7 +20419,7 @@ _loop1_82_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_82[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_80[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -20514,14 +20437,14 @@ _loop1_82_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop1_82_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop1_80_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop1_83: lambda_param_no_default
+// _loop1_81: lambda_param_no_default
 static asdl_seq *
-_loop1_83_rule(Parser *p)
+_loop1_81_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -20545,7 +20468,7 @@ _loop1_83_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_83[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default"));
+        D(fprintf(stderr, "%*c> _loop1_81[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default"));
         arg_ty lambda_param_no_default_var;
         while (
             (lambda_param_no_default_var = lambda_param_no_default_rule(p))  // lambda_param_no_default
@@ -20567,7 +20490,7 @@ _loop1_83_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_83[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_81[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -20585,14 +20508,14 @@ _loop1_83_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop1_83_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop1_81_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop0_84: lambda_param_no_default
+// _loop0_82: lambda_param_no_default
 static asdl_seq *
-_loop0_84_rule(Parser *p)
+_loop0_82_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -20616,7 +20539,7 @@ _loop0_84_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_84[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default"));
+        D(fprintf(stderr, "%*c> _loop0_82[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default"));
         arg_ty lambda_param_no_default_var;
         while (
             (lambda_param_no_default_var = lambda_param_no_default_rule(p))  // lambda_param_no_default
@@ -20638,7 +20561,7 @@ _loop0_84_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_84[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_82[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -20651,14 +20574,14 @@ _loop0_84_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_84_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_82_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop1_85: lambda_param_with_default
+// _loop1_83: lambda_param_with_default
 static asdl_seq *
-_loop1_85_rule(Parser *p)
+_loop1_83_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -20682,7 +20605,7 @@ _loop1_85_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_85[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default"));
+        D(fprintf(stderr, "%*c> _loop1_83[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default"));
         NameDefaultPair* lambda_param_with_default_var;
         while (
             (lambda_param_with_default_var = lambda_param_with_default_rule(p))  // lambda_param_with_default
@@ -20704,7 +20627,7 @@ _loop1_85_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_85[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_83[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_with_default"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -20722,14 +20645,14 @@ _loop1_85_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop1_85_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop1_83_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop0_86: lambda_param_no_default
+// _loop0_84: lambda_param_no_default
 static asdl_seq *
-_loop0_86_rule(Parser *p)
+_loop0_84_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -20753,7 +20676,7 @@ _loop0_86_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_86[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default"));
+        D(fprintf(stderr, "%*c> _loop0_84[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default"));
         arg_ty lambda_param_no_default_var;
         while (
             (lambda_param_no_default_var = lambda_param_no_default_rule(p))  // lambda_param_no_default
@@ -20775,7 +20698,7 @@ _loop0_86_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_86[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_84[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -20788,14 +20711,14 @@ _loop0_86_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_86_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_84_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop1_87: lambda_param_with_default
+// _loop1_85: lambda_param_with_default
 static asdl_seq *
-_loop1_87_rule(Parser *p)
+_loop1_85_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -20819,7 +20742,7 @@ _loop1_87_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_87[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default"));
+        D(fprintf(stderr, "%*c> _loop1_85[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default"));
         NameDefaultPair* lambda_param_with_default_var;
         while (
             (lambda_param_with_default_var = lambda_param_with_default_rule(p))  // lambda_param_with_default
@@ -20841,7 +20764,7 @@ _loop1_87_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_87[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_85[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_with_default"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -20859,14 +20782,14 @@ _loop1_87_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop1_87_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop1_85_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop0_88: lambda_param_maybe_default
+// _loop0_86: lambda_param_maybe_default
 static asdl_seq *
-_loop0_88_rule(Parser *p)
+_loop0_86_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -20890,7 +20813,7 @@ _loop0_88_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_88[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default"));
+        D(fprintf(stderr, "%*c> _loop0_86[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default"));
         NameDefaultPair* lambda_param_maybe_default_var;
         while (
             (lambda_param_maybe_default_var = lambda_param_maybe_default_rule(p))  // lambda_param_maybe_default
@@ -20912,7 +20835,7 @@ _loop0_88_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_88[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_86[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_maybe_default"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -20925,14 +20848,14 @@ _loop0_88_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_88_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_86_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop1_89: lambda_param_maybe_default
+// _loop1_87: lambda_param_maybe_default
 static asdl_seq *
-_loop1_89_rule(Parser *p)
+_loop1_87_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -20956,7 +20879,7 @@ _loop1_89_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_89[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default"));
+        D(fprintf(stderr, "%*c> _loop1_87[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default"));
         NameDefaultPair* lambda_param_maybe_default_var;
         while (
             (lambda_param_maybe_default_var = lambda_param_maybe_default_rule(p))  // lambda_param_maybe_default
@@ -20978,7 +20901,7 @@ _loop1_89_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_89[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_87[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_maybe_default"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -20996,14 +20919,14 @@ _loop1_89_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop1_89_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop1_87_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop1_90: ('or' conjunction)
+// _loop1_88: ('or' conjunction)
 static asdl_seq *
-_loop1_90_rule(Parser *p)
+_loop1_88_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -21027,13 +20950,13 @@ _loop1_90_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_90[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('or' conjunction)"));
-        void *_tmp_145_var;
+        D(fprintf(stderr, "%*c> _loop1_88[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('or' conjunction)"));
+        void *_tmp_143_var;
         while (
-            (_tmp_145_var = _tmp_145_rule(p))  // 'or' conjunction
+            (_tmp_143_var = _tmp_143_rule(p))  // 'or' conjunction
         )
         {
-            _res = _tmp_145_var;
+            _res = _tmp_143_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -21049,7 +20972,7 @@ _loop1_90_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_90[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_88[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "('or' conjunction)"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -21067,14 +20990,14 @@ _loop1_90_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop1_90_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop1_88_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop1_91: ('and' inversion)
+// _loop1_89: ('and' inversion)
 static asdl_seq *
-_loop1_91_rule(Parser *p)
+_loop1_89_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -21098,13 +21021,13 @@ _loop1_91_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_91[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('and' inversion)"));
-        void *_tmp_146_var;
+        D(fprintf(stderr, "%*c> _loop1_89[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('and' inversion)"));
+        void *_tmp_144_var;
         while (
-            (_tmp_146_var = _tmp_146_rule(p))  // 'and' inversion
+            (_tmp_144_var = _tmp_144_rule(p))  // 'and' inversion
         )
         {
-            _res = _tmp_146_var;
+            _res = _tmp_144_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -21120,7 +21043,7 @@ _loop1_91_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_91[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_89[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "('and' inversion)"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -21138,14 +21061,14 @@ _loop1_91_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop1_91_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop1_89_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop1_92: compare_op_bitwise_or_pair
+// _loop1_90: compare_op_bitwise_or_pair
 static asdl_seq *
-_loop1_92_rule(Parser *p)
+_loop1_90_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -21169,7 +21092,7 @@ _loop1_92_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_92[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "compare_op_bitwise_or_pair"));
+        D(fprintf(stderr, "%*c> _loop1_90[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "compare_op_bitwise_or_pair"));
         CmpopExprPair* compare_op_bitwise_or_pair_var;
         while (
             (compare_op_bitwise_or_pair_var = compare_op_bitwise_or_pair_rule(p))  // compare_op_bitwise_or_pair
@@ -21191,7 +21114,7 @@ _loop1_92_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_92[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_90[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "compare_op_bitwise_or_pair"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -21209,14 +21132,14 @@ _loop1_92_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop1_92_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop1_90_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _tmp_93: '!='
+// _tmp_91: '!='
 static void *
-_tmp_93_rule(Parser *p)
+_tmp_91_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -21230,14 +21153,14 @@ _tmp_93_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_93[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!='"));
+        D(fprintf(stderr, "%*c> _tmp_91[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!='"));
         Token * tok;
         if (
             (tok = _PyPegen_expect_token(p, 28))  // token='!='
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_93[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!='"));
-            _res = _PyPegen_check_barry_as_flufl ( p ) ? NULL : tok;
+            D(fprintf(stderr, "%*c+ _tmp_91[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!='"));
+            _res = _PyPegen_check_barry_as_flufl ( p , tok ) ? NULL : tok;
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
                 D(p->level--);
@@ -21246,7 +21169,7 @@ _tmp_93_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_93[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_91[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'!='"));
     }
     _res = NULL;
@@ -21255,9 +21178,9 @@ _tmp_93_rule(Parser *p)
     return _res;
 }
 
-// _loop0_95: ',' slice
+// _loop0_93: ',' slice
 static asdl_seq *
-_loop0_95_rule(Parser *p)
+_loop0_93_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -21281,7 +21204,7 @@ _loop0_95_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_95[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' slice"));
+        D(fprintf(stderr, "%*c> _loop0_93[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' slice"));
         Token * _literal;
         expr_ty elem;
         while (
@@ -21312,7 +21235,7 @@ _loop0_95_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_95[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_93[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' slice"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -21325,14 +21248,14 @@ _loop0_95_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_95_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_93_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _gather_94: slice _loop0_95
+// _gather_92: slice _loop0_93
 static asdl_seq *
-_gather_94_rule(Parser *p)
+_gather_92_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -21341,27 +21264,27 @@ _gather_94_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // slice _loop0_95
+    { // slice _loop0_93
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_94[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slice _loop0_95"));
+        D(fprintf(stderr, "%*c> _gather_92[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slice _loop0_93"));
         expr_ty elem;
         asdl_seq * seq;
         if (
             (elem = slice_rule(p))  // slice
             &&
-            (seq = _loop0_95_rule(p))  // _loop0_95
+            (seq = _loop0_93_rule(p))  // _loop0_93
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_94[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slice _loop0_95"));
+            D(fprintf(stderr, "%*c+ _gather_92[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slice _loop0_93"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_94[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slice _loop0_95"));
+        D(fprintf(stderr, "%*c%s _gather_92[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slice _loop0_93"));
     }
     _res = NULL;
   done:
@@ -21369,9 +21292,9 @@ _gather_94_rule(Parser *p)
     return _res;
 }
 
-// _tmp_96: ':' expression?
+// _tmp_94: ':' expression?
 static void *
-_tmp_96_rule(Parser *p)
+_tmp_94_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -21385,7 +21308,7 @@ _tmp_96_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_96[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':' expression?"));
+        D(fprintf(stderr, "%*c> _tmp_94[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':' expression?"));
         Token * _literal;
         void *d;
         if (
@@ -21394,7 +21317,7 @@ _tmp_96_rule(Parser *p)
             (d = expression_rule(p), 1)  // expression?
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_96[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':' expression?"));
+            D(fprintf(stderr, "%*c+ _tmp_94[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':' expression?"));
             _res = d;
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -21404,7 +21327,7 @@ _tmp_96_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_96[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_94[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':' expression?"));
     }
     _res = NULL;
@@ -21413,9 +21336,9 @@ _tmp_96_rule(Parser *p)
     return _res;
 }
 
-// _tmp_97: tuple | group | genexp
+// _tmp_95: tuple | group | genexp
 static void *
-_tmp_97_rule(Parser *p)
+_tmp_95_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -21429,18 +21352,18 @@ _tmp_97_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_97[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tuple"));
+        D(fprintf(stderr, "%*c> _tmp_95[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tuple"));
         expr_ty tuple_var;
         if (
             (tuple_var = tuple_rule(p))  // tuple
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_97[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "tuple"));
+            D(fprintf(stderr, "%*c+ _tmp_95[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "tuple"));
             _res = tuple_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_97[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_95[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "tuple"));
     }
     { // group
@@ -21448,18 +21371,18 @@ _tmp_97_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_97[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "group"));
+        D(fprintf(stderr, "%*c> _tmp_95[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "group"));
         expr_ty group_var;
         if (
             (group_var = group_rule(p))  // group
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_97[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "group"));
+            D(fprintf(stderr, "%*c+ _tmp_95[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "group"));
             _res = group_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_97[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_95[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "group"));
     }
     { // genexp
@@ -21467,18 +21390,18 @@ _tmp_97_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_97[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "genexp"));
+        D(fprintf(stderr, "%*c> _tmp_95[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "genexp"));
         expr_ty genexp_var;
         if (
             (genexp_var = genexp_rule(p))  // genexp
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_97[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "genexp"));
+            D(fprintf(stderr, "%*c+ _tmp_95[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "genexp"));
             _res = genexp_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_97[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_95[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "genexp"));
     }
     _res = NULL;
@@ -21487,9 +21410,9 @@ _tmp_97_rule(Parser *p)
     return _res;
 }
 
-// _tmp_98: list | listcomp
+// _tmp_96: list | listcomp
 static void *
-_tmp_98_rule(Parser *p)
+_tmp_96_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -21503,18 +21426,18 @@ _tmp_98_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_98[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "list"));
+        D(fprintf(stderr, "%*c> _tmp_96[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "list"));
         expr_ty list_var;
         if (
             (list_var = list_rule(p))  // list
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_98[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "list"));
+            D(fprintf(stderr, "%*c+ _tmp_96[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "list"));
             _res = list_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_98[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_96[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "list"));
     }
     { // listcomp
@@ -21522,18 +21445,18 @@ _tmp_98_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_98[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "listcomp"));
+        D(fprintf(stderr, "%*c> _tmp_96[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "listcomp"));
         expr_ty listcomp_var;
         if (
             (listcomp_var = listcomp_rule(p))  // listcomp
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_98[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "listcomp"));
+            D(fprintf(stderr, "%*c+ _tmp_96[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "listcomp"));
             _res = listcomp_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_98[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_96[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "listcomp"));
     }
     _res = NULL;
@@ -21542,9 +21465,9 @@ _tmp_98_rule(Parser *p)
     return _res;
 }
 
-// _tmp_99: dict | set | dictcomp | setcomp
+// _tmp_97: dict | set | dictcomp | setcomp
 static void *
-_tmp_99_rule(Parser *p)
+_tmp_97_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -21558,18 +21481,18 @@ _tmp_99_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_99[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "dict"));
+        D(fprintf(stderr, "%*c> _tmp_97[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "dict"));
         expr_ty dict_var;
         if (
             (dict_var = dict_rule(p))  // dict
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_99[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "dict"));
+            D(fprintf(stderr, "%*c+ _tmp_97[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "dict"));
             _res = dict_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_99[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_97[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "dict"));
     }
     { // set
@@ -21577,18 +21500,18 @@ _tmp_99_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_99[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "set"));
+        D(fprintf(stderr, "%*c> _tmp_97[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "set"));
         expr_ty set_var;
         if (
             (set_var = set_rule(p))  // set
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_99[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "set"));
+            D(fprintf(stderr, "%*c+ _tmp_97[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "set"));
             _res = set_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_99[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_97[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "set"));
     }
     { // dictcomp
@@ -21596,18 +21519,18 @@ _tmp_99_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_99[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "dictcomp"));
+        D(fprintf(stderr, "%*c> _tmp_97[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "dictcomp"));
         expr_ty dictcomp_var;
         if (
             (dictcomp_var = dictcomp_rule(p))  // dictcomp
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_99[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "dictcomp"));
+            D(fprintf(stderr, "%*c+ _tmp_97[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "dictcomp"));
             _res = dictcomp_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_99[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_97[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "dictcomp"));
     }
     { // setcomp
@@ -21615,18 +21538,18 @@ _tmp_99_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_99[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "setcomp"));
+        D(fprintf(stderr, "%*c> _tmp_97[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "setcomp"));
         expr_ty setcomp_var;
         if (
             (setcomp_var = setcomp_rule(p))  // setcomp
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_99[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "setcomp"));
+            D(fprintf(stderr, "%*c+ _tmp_97[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "setcomp"));
             _res = setcomp_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_99[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_97[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "setcomp"));
     }
     _res = NULL;
@@ -21635,9 +21558,9 @@ _tmp_99_rule(Parser *p)
     return _res;
 }
 
-// _loop1_100: STRING
+// _loop1_98: STRING
 static asdl_seq *
-_loop1_100_rule(Parser *p)
+_loop1_98_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -21661,7 +21584,7 @@ _loop1_100_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_100[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "STRING"));
+        D(fprintf(stderr, "%*c> _loop1_98[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "STRING"));
         expr_ty string_var;
         while (
             (string_var = _PyPegen_string_token(p))  // STRING
@@ -21683,7 +21606,7 @@ _loop1_100_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_100[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_98[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "STRING"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -21701,14 +21624,14 @@ _loop1_100_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop1_100_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop1_98_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _tmp_101: star_named_expression ',' star_named_expressions?
+// _tmp_99: star_named_expression ',' star_named_expressions?
 static void *
-_tmp_101_rule(Parser *p)
+_tmp_99_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -21722,7 +21645,7 @@ _tmp_101_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_101[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expression ',' star_named_expressions?"));
+        D(fprintf(stderr, "%*c> _tmp_99[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expression ',' star_named_expressions?"));
         Token * _literal;
         expr_ty y;
         void *z;
@@ -21734,7 +21657,7 @@ _tmp_101_rule(Parser *p)
             (z = star_named_expressions_rule(p), 1)  // star_named_expressions?
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_101[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_named_expression ',' star_named_expressions?"));
+            D(fprintf(stderr, "%*c+ _tmp_99[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_named_expression ',' star_named_expressions?"));
             _res = _PyPegen_seq_insert_in_front ( p , y , z );
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -21744,7 +21667,7 @@ _tmp_101_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_101[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_99[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_named_expression ',' star_named_expressions?"));
     }
     _res = NULL;
@@ -21753,9 +21676,9 @@ _tmp_101_rule(Parser *p)
     return _res;
 }
 
-// _tmp_102: yield_expr | named_expression
+// _tmp_100: yield_expr | named_expression
 static void *
-_tmp_102_rule(Parser *p)
+_tmp_100_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -21769,18 +21692,18 @@ _tmp_102_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_102[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "yield_expr"));
+        D(fprintf(stderr, "%*c> _tmp_100[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "yield_expr"));
         expr_ty yield_expr_var;
         if (
             (yield_expr_var = yield_expr_rule(p))  // yield_expr
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_102[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "yield_expr"));
+            D(fprintf(stderr, "%*c+ _tmp_100[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "yield_expr"));
             _res = yield_expr_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_102[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_100[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "yield_expr"));
     }
     { // named_expression
@@ -21788,18 +21711,18 @@ _tmp_102_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_102[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "named_expression"));
+        D(fprintf(stderr, "%*c> _tmp_100[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "named_expression"));
         expr_ty named_expression_var;
         if (
             (named_expression_var = named_expression_rule(p))  // named_expression
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_102[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "named_expression"));
+            D(fprintf(stderr, "%*c+ _tmp_100[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "named_expression"));
             _res = named_expression_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_102[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_100[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "named_expression"));
     }
     _res = NULL;
@@ -21808,9 +21731,9 @@ _tmp_102_rule(Parser *p)
     return _res;
 }
 
-// _loop0_104: ',' double_starred_kvpair
+// _loop0_102: ',' double_starred_kvpair
 static asdl_seq *
-_loop0_104_rule(Parser *p)
+_loop0_102_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -21834,7 +21757,7 @@ _loop0_104_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_104[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' double_starred_kvpair"));
+        D(fprintf(stderr, "%*c> _loop0_102[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' double_starred_kvpair"));
         Token * _literal;
         KeyValuePair* elem;
         while (
@@ -21865,7 +21788,7 @@ _loop0_104_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_104[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_102[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' double_starred_kvpair"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -21878,14 +21801,14 @@ _loop0_104_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_104_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_102_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _gather_103: double_starred_kvpair _loop0_104
+// _gather_101: double_starred_kvpair _loop0_102
 static asdl_seq *
-_gather_103_rule(Parser *p)
+_gather_101_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -21894,27 +21817,27 @@ _gather_103_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // double_starred_kvpair _loop0_104
+    { // double_starred_kvpair _loop0_102
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_103[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_104"));
+        D(fprintf(stderr, "%*c> _gather_101[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_102"));
         KeyValuePair* elem;
         asdl_seq * seq;
         if (
             (elem = double_starred_kvpair_rule(p))  // double_starred_kvpair
             &&
-            (seq = _loop0_104_rule(p))  // _loop0_104
+            (seq = _loop0_102_rule(p))  // _loop0_102
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_103[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_104"));
+            D(fprintf(stderr, "%*c+ _gather_101[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_102"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_103[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "double_starred_kvpair _loop0_104"));
+        D(fprintf(stderr, "%*c%s _gather_101[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "double_starred_kvpair _loop0_102"));
     }
     _res = NULL;
   done:
@@ -21922,9 +21845,9 @@ _gather_103_rule(Parser *p)
     return _res;
 }
 
-// _loop1_105: for_if_clause
+// _loop1_103: for_if_clause
 static asdl_seq *
-_loop1_105_rule(Parser *p)
+_loop1_103_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -21948,7 +21871,7 @@ _loop1_105_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_105[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "for_if_clause"));
+        D(fprintf(stderr, "%*c> _loop1_103[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "for_if_clause"));
         comprehension_ty for_if_clause_var;
         while (
             (for_if_clause_var = for_if_clause_rule(p))  // for_if_clause
@@ -21970,7 +21893,7 @@ _loop1_105_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_105[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_103[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "for_if_clause"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -21988,14 +21911,14 @@ _loop1_105_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop1_105_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop1_103_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop0_106: ('if' disjunction)
+// _loop0_104: ('if' disjunction)
 static asdl_seq *
-_loop0_106_rule(Parser *p)
+_loop0_104_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -22019,13 +21942,13 @@ _loop0_106_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_106[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('if' disjunction)"));
-        void *_tmp_147_var;
+        D(fprintf(stderr, "%*c> _loop0_104[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('if' disjunction)"));
+        void *_tmp_145_var;
         while (
-            (_tmp_147_var = _tmp_147_rule(p))  // 'if' disjunction
+            (_tmp_145_var = _tmp_145_rule(p))  // 'if' disjunction
         )
         {
-            _res = _tmp_147_var;
+            _res = _tmp_145_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -22041,7 +21964,7 @@ _loop0_106_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_106[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_104[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "('if' disjunction)"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -22054,14 +21977,14 @@ _loop0_106_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_106_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_104_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop0_107: ('if' disjunction)
+// _loop0_105: ('if' disjunction)
 static asdl_seq *
-_loop0_107_rule(Parser *p)
+_loop0_105_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -22085,13 +22008,13 @@ _loop0_107_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_107[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('if' disjunction)"));
-        void *_tmp_148_var;
+        D(fprintf(stderr, "%*c> _loop0_105[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('if' disjunction)"));
+        void *_tmp_146_var;
         while (
-            (_tmp_148_var = _tmp_148_rule(p))  // 'if' disjunction
+            (_tmp_146_var = _tmp_146_rule(p))  // 'if' disjunction
         )
         {
-            _res = _tmp_148_var;
+            _res = _tmp_146_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -22107,7 +22030,7 @@ _loop0_107_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_107[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_105[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "('if' disjunction)"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -22120,14 +22043,14 @@ _loop0_107_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_107_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_105_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop0_109: ',' (starred_expression | named_expression !'=')
+// _loop0_107: ',' (starred_expression | named_expression !'=')
 static asdl_seq *
-_loop0_109_rule(Parser *p)
+_loop0_107_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -22151,13 +22074,13 @@ _loop0_109_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_109[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (starred_expression | named_expression !'=')"));
+        D(fprintf(stderr, "%*c> _loop0_107[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (starred_expression | named_expression !'=')"));
         Token * _literal;
         void *elem;
         while (
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
             &&
-            (elem = _tmp_149_rule(p))  // starred_expression | named_expression !'='
+            (elem = _tmp_147_rule(p))  // starred_expression | named_expression !'='
         )
         {
             _res = elem;
@@ -22182,7 +22105,7 @@ _loop0_109_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_109[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_107[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (starred_expression | named_expression !'=')"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -22195,14 +22118,14 @@ _loop0_109_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_109_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_107_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _gather_108: (starred_expression | named_expression !'=') _loop0_109
+// _gather_106: (starred_expression | named_expression !'=') _loop0_107
 static asdl_seq *
-_gather_108_rule(Parser *p)
+_gather_106_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -22211,27 +22134,27 @@ _gather_108_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // (starred_expression | named_expression !'=') _loop0_109
+    { // (starred_expression | named_expression !'=') _loop0_107
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_108[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(starred_expression | named_expression !'=') _loop0_109"));
+        D(fprintf(stderr, "%*c> _gather_106[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(starred_expression | named_expression !'=') _loop0_107"));
         void *elem;
         asdl_seq * seq;
         if (
-            (elem = _tmp_149_rule(p))  // starred_expression | named_expression !'='
+            (elem = _tmp_147_rule(p))  // starred_expression | named_expression !'='
             &&
-            (seq = _loop0_109_rule(p))  // _loop0_109
+            (seq = _loop0_107_rule(p))  // _loop0_107
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_108[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(starred_expression | named_expression !'=') _loop0_109"));
+            D(fprintf(stderr, "%*c+ _gather_106[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(starred_expression | named_expression !'=') _loop0_107"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_108[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(starred_expression | named_expression !'=') _loop0_109"));
+        D(fprintf(stderr, "%*c%s _gather_106[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(starred_expression | named_expression !'=') _loop0_107"));
     }
     _res = NULL;
   done:
@@ -22239,9 +22162,9 @@ _gather_108_rule(Parser *p)
     return _res;
 }
 
-// _tmp_110: ',' kwargs
+// _tmp_108: ',' kwargs
 static void *
-_tmp_110_rule(Parser *p)
+_tmp_108_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -22255,7 +22178,7 @@ _tmp_110_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_110[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwargs"));
+        D(fprintf(stderr, "%*c> _tmp_108[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwargs"));
         Token * _literal;
         asdl_seq* k;
         if (
@@ -22264,7 +22187,7 @@ _tmp_110_rule(Parser *p)
             (k = kwargs_rule(p))  // kwargs
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_110[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' kwargs"));
+            D(fprintf(stderr, "%*c+ _tmp_108[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' kwargs"));
             _res = k;
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -22274,7 +22197,7 @@ _tmp_110_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_110[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_108[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' kwargs"));
     }
     _res = NULL;
@@ -22283,9 +22206,9 @@ _tmp_110_rule(Parser *p)
     return _res;
 }
 
-// _loop0_112: ',' kwarg_or_starred
+// _loop0_110: ',' kwarg_or_starred
 static asdl_seq *
-_loop0_112_rule(Parser *p)
+_loop0_110_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -22309,7 +22232,7 @@ _loop0_112_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_112[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_starred"));
+        D(fprintf(stderr, "%*c> _loop0_110[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_starred"));
         Token * _literal;
         KeywordOrStarred* elem;
         while (
@@ -22340,7 +22263,7 @@ _loop0_112_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_112[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_110[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' kwarg_or_starred"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -22353,14 +22276,14 @@ _loop0_112_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_112_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_110_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _gather_111: kwarg_or_starred _loop0_112
+// _gather_109: kwarg_or_starred _loop0_110
 static asdl_seq *
-_gather_111_rule(Parser *p)
+_gather_109_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -22369,27 +22292,27 @@ _gather_111_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // kwarg_or_starred _loop0_112
+    { // kwarg_or_starred _loop0_110
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_111[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_112"));
+        D(fprintf(stderr, "%*c> _gather_109[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_110"));
         KeywordOrStarred* elem;
         asdl_seq * seq;
         if (
             (elem = kwarg_or_starred_rule(p))  // kwarg_or_starred
             &&
-            (seq = _loop0_112_rule(p))  // _loop0_112
+            (seq = _loop0_110_rule(p))  // _loop0_110
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_111[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_112"));
+            D(fprintf(stderr, "%*c+ _gather_109[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_110"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_111[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_starred _loop0_112"));
+        D(fprintf(stderr, "%*c%s _gather_109[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_starred _loop0_110"));
     }
     _res = NULL;
   done:
@@ -22397,9 +22320,9 @@ _gather_111_rule(Parser *p)
     return _res;
 }
 
-// _loop0_114: ',' kwarg_or_double_starred
+// _loop0_112: ',' kwarg_or_double_starred
 static asdl_seq *
-_loop0_114_rule(Parser *p)
+_loop0_112_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -22423,7 +22346,7 @@ _loop0_114_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_114[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_double_starred"));
+        D(fprintf(stderr, "%*c> _loop0_112[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_double_starred"));
         Token * _literal;
         KeywordOrStarred* elem;
         while (
@@ -22454,7 +22377,7 @@ _loop0_114_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_114[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_112[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' kwarg_or_double_starred"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -22467,14 +22390,14 @@ _loop0_114_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_114_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_112_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _gather_113: kwarg_or_double_starred _loop0_114
+// _gather_111: kwarg_or_double_starred _loop0_112
 static asdl_seq *
-_gather_113_rule(Parser *p)
+_gather_111_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -22483,27 +22406,27 @@ _gather_113_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // kwarg_or_double_starred _loop0_114
+    { // kwarg_or_double_starred _loop0_112
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_113[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_114"));
+        D(fprintf(stderr, "%*c> _gather_111[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_112"));
         KeywordOrStarred* elem;
         asdl_seq * seq;
         if (
             (elem = kwarg_or_double_starred_rule(p))  // kwarg_or_double_starred
             &&
-            (seq = _loop0_114_rule(p))  // _loop0_114
+            (seq = _loop0_112_rule(p))  // _loop0_112
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_113[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_114"));
+            D(fprintf(stderr, "%*c+ _gather_111[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_112"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_113[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_double_starred _loop0_114"));
+        D(fprintf(stderr, "%*c%s _gather_111[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_double_starred _loop0_112"));
     }
     _res = NULL;
   done:
@@ -22511,9 +22434,9 @@ _gather_113_rule(Parser *p)
     return _res;
 }
 
-// _loop0_116: ',' kwarg_or_starred
+// _loop0_114: ',' kwarg_or_starred
 static asdl_seq *
-_loop0_116_rule(Parser *p)
+_loop0_114_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -22537,7 +22460,7 @@ _loop0_116_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_116[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_starred"));
+        D(fprintf(stderr, "%*c> _loop0_114[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_starred"));
         Token * _literal;
         KeywordOrStarred* elem;
         while (
@@ -22568,7 +22491,7 @@ _loop0_116_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_116[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_114[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' kwarg_or_starred"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -22581,14 +22504,14 @@ _loop0_116_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_116_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_114_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _gather_115: kwarg_or_starred _loop0_116
+// _gather_113: kwarg_or_starred _loop0_114
 static asdl_seq *
-_gather_115_rule(Parser *p)
+_gather_113_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -22597,27 +22520,27 @@ _gather_115_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // kwarg_or_starred _loop0_116
+    { // kwarg_or_starred _loop0_114
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_115[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_116"));
+        D(fprintf(stderr, "%*c> _gather_113[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_114"));
         KeywordOrStarred* elem;
         asdl_seq * seq;
         if (
             (elem = kwarg_or_starred_rule(p))  // kwarg_or_starred
             &&
-            (seq = _loop0_116_rule(p))  // _loop0_116
+            (seq = _loop0_114_rule(p))  // _loop0_114
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_115[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_116"));
+            D(fprintf(stderr, "%*c+ _gather_113[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_114"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_115[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_starred _loop0_116"));
+        D(fprintf(stderr, "%*c%s _gather_113[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_starred _loop0_114"));
     }
     _res = NULL;
   done:
@@ -22625,9 +22548,9 @@ _gather_115_rule(Parser *p)
     return _res;
 }
 
-// _loop0_118: ',' kwarg_or_double_starred
+// _loop0_116: ',' kwarg_or_double_starred
 static asdl_seq *
-_loop0_118_rule(Parser *p)
+_loop0_116_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -22651,7 +22574,7 @@ _loop0_118_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_118[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_double_starred"));
+        D(fprintf(stderr, "%*c> _loop0_116[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_double_starred"));
         Token * _literal;
         KeywordOrStarred* elem;
         while (
@@ -22682,7 +22605,7 @@ _loop0_118_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_118[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_116[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' kwarg_or_double_starred"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -22695,14 +22618,14 @@ _loop0_118_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_118_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_116_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _gather_117: kwarg_or_double_starred _loop0_118
+// _gather_115: kwarg_or_double_starred _loop0_116
 static asdl_seq *
-_gather_117_rule(Parser *p)
+_gather_115_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -22711,27 +22634,27 @@ _gather_117_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // kwarg_or_double_starred _loop0_118
+    { // kwarg_or_double_starred _loop0_116
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_117[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_118"));
+        D(fprintf(stderr, "%*c> _gather_115[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_116"));
         KeywordOrStarred* elem;
         asdl_seq * seq;
         if (
             (elem = kwarg_or_double_starred_rule(p))  // kwarg_or_double_starred
             &&
-            (seq = _loop0_118_rule(p))  // _loop0_118
+            (seq = _loop0_116_rule(p))  // _loop0_116
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_117[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_118"));
+            D(fprintf(stderr, "%*c+ _gather_115[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_116"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_117[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_double_starred _loop0_118"));
+        D(fprintf(stderr, "%*c%s _gather_115[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_double_starred _loop0_116"));
     }
     _res = NULL;
   done:
@@ -22739,9 +22662,9 @@ _gather_117_rule(Parser *p)
     return _res;
 }
 
-// _loop0_119: (',' star_target)
+// _loop0_117: (',' star_target)
 static asdl_seq *
-_loop0_119_rule(Parser *p)
+_loop0_117_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -22765,13 +22688,13 @@ _loop0_119_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_119[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_target)"));
-        void *_tmp_150_var;
+        D(fprintf(stderr, "%*c> _loop0_117[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_target)"));
+        void *_tmp_148_var;
         while (
-            (_tmp_150_var = _tmp_150_rule(p))  // ',' star_target
+            (_tmp_148_var = _tmp_148_rule(p))  // ',' star_target
         )
         {
-            _res = _tmp_150_var;
+            _res = _tmp_148_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -22787,7 +22710,7 @@ _loop0_119_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_119[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_117[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(',' star_target)"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -22800,14 +22723,14 @@ _loop0_119_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_119_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_117_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop0_121: ',' star_target
+// _loop0_119: ',' star_target
 static asdl_seq *
-_loop0_121_rule(Parser *p)
+_loop0_119_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -22831,7 +22754,7 @@ _loop0_121_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_121[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target"));
+        D(fprintf(stderr, "%*c> _loop0_119[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target"));
         Token * _literal;
         expr_ty elem;
         while (
@@ -22862,7 +22785,7 @@ _loop0_121_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_121[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_119[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_target"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -22875,14 +22798,14 @@ _loop0_121_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_121_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_119_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _gather_120: star_target _loop0_121
+// _gather_118: star_target _loop0_119
 static asdl_seq *
-_gather_120_rule(Parser *p)
+_gather_118_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -22891,27 +22814,27 @@ _gather_120_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // star_target _loop0_121
+    { // star_target _loop0_119
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_120[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_target _loop0_121"));
+        D(fprintf(stderr, "%*c> _gather_118[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_target _loop0_119"));
         expr_ty elem;
         asdl_seq * seq;
         if (
             (elem = star_target_rule(p))  // star_target
             &&
-            (seq = _loop0_121_rule(p))  // _loop0_121
+            (seq = _loop0_119_rule(p))  // _loop0_119
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_120[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_target _loop0_121"));
+            D(fprintf(stderr, "%*c+ _gather_118[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_target _loop0_119"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_120[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_target _loop0_121"));
+        D(fprintf(stderr, "%*c%s _gather_118[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_target _loop0_119"));
     }
     _res = NULL;
   done:
@@ -22919,9 +22842,9 @@ _gather_120_rule(Parser *p)
     return _res;
 }
 
-// _tmp_122: !'*' star_target
+// _tmp_120: !'*' star_target
 static void *
-_tmp_122_rule(Parser *p)
+_tmp_120_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -22935,7 +22858,7 @@ _tmp_122_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_122[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "!'*' star_target"));
+        D(fprintf(stderr, "%*c> _tmp_120[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "!'*' star_target"));
         expr_ty star_target_var;
         if (
             _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 16)  // token='*'
@@ -22943,12 +22866,12 @@ _tmp_122_rule(Parser *p)
             (star_target_var = star_target_rule(p))  // star_target
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_122[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!'*' star_target"));
+            D(fprintf(stderr, "%*c+ _tmp_120[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!'*' star_target"));
             _res = star_target_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_122[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_120[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "!'*' star_target"));
     }
     _res = NULL;
@@ -22957,9 +22880,9 @@ _tmp_122_rule(Parser *p)
     return _res;
 }
 
-// _loop0_124: ',' del_target
+// _loop0_122: ',' del_target
 static asdl_seq *
-_loop0_124_rule(Parser *p)
+_loop0_122_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -22983,7 +22906,7 @@ _loop0_124_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_124[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' del_target"));
+        D(fprintf(stderr, "%*c> _loop0_122[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' del_target"));
         Token * _literal;
         expr_ty elem;
         while (
@@ -23014,7 +22937,7 @@ _loop0_124_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_124[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_122[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' del_target"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -23027,14 +22950,14 @@ _loop0_124_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_124_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_122_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _gather_123: del_target _loop0_124
+// _gather_121: del_target _loop0_122
 static asdl_seq *
-_gather_123_rule(Parser *p)
+_gather_121_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -23043,27 +22966,27 @@ _gather_123_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // del_target _loop0_124
+    { // del_target _loop0_122
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_123[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "del_target _loop0_124"));
+        D(fprintf(stderr, "%*c> _gather_121[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "del_target _loop0_122"));
         expr_ty elem;
         asdl_seq * seq;
         if (
             (elem = del_target_rule(p))  // del_target
             &&
-            (seq = _loop0_124_rule(p))  // _loop0_124
+            (seq = _loop0_122_rule(p))  // _loop0_122
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_123[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "del_target _loop0_124"));
+            D(fprintf(stderr, "%*c+ _gather_121[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "del_target _loop0_122"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_123[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "del_target _loop0_124"));
+        D(fprintf(stderr, "%*c%s _gather_121[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "del_target _loop0_122"));
     }
     _res = NULL;
   done:
@@ -23071,9 +22994,9 @@ _gather_123_rule(Parser *p)
     return _res;
 }
 
-// _loop0_126: ',' target
+// _loop0_124: ',' target
 static asdl_seq *
-_loop0_126_rule(Parser *p)
+_loop0_124_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -23097,7 +23020,7 @@ _loop0_126_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_126[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' target"));
+        D(fprintf(stderr, "%*c> _loop0_124[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' target"));
         Token * _literal;
         expr_ty elem;
         while (
@@ -23128,7 +23051,7 @@ _loop0_126_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_126[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_124[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' target"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -23141,14 +23064,14 @@ _loop0_126_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_126_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_124_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _gather_125: target _loop0_126
+// _gather_123: target _loop0_124
 static asdl_seq *
-_gather_125_rule(Parser *p)
+_gather_123_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -23157,27 +23080,27 @@ _gather_125_rule(Parser *p)
     }
     asdl_seq * _res = NULL;
     int _mark = p->mark;
-    { // target _loop0_126
+    { // target _loop0_124
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _gather_125[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "target _loop0_126"));
+        D(fprintf(stderr, "%*c> _gather_123[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "target _loop0_124"));
         expr_ty elem;
         asdl_seq * seq;
         if (
             (elem = target_rule(p))  // target
             &&
-            (seq = _loop0_126_rule(p))  // _loop0_126
+            (seq = _loop0_124_rule(p))  // _loop0_124
         )
         {
-            D(fprintf(stderr, "%*c+ _gather_125[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "target _loop0_126"));
+            D(fprintf(stderr, "%*c+ _gather_123[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "target _loop0_124"));
             _res = _PyPegen_seq_insert_in_front(p, elem, seq);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _gather_125[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "target _loop0_126"));
+        D(fprintf(stderr, "%*c%s _gather_123[%d-%d]: %s failed!\n", p->level, ' ',
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "target _loop0_124"));
     }
     _res = NULL;
   done:
@@ -23185,9 +23108,9 @@ _gather_125_rule(Parser *p)
     return _res;
 }
 
-// _tmp_127: args | expression for_if_clauses
+// _tmp_125: args | expression for_if_clauses
 static void *
-_tmp_127_rule(Parser *p)
+_tmp_125_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -23201,18 +23124,18 @@ _tmp_127_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_127[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args"));
+        D(fprintf(stderr, "%*c> _tmp_125[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args"));
         expr_ty args_var;
         if (
             (args_var = args_rule(p))  // args
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_127[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args"));
+            D(fprintf(stderr, "%*c+ _tmp_125[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args"));
             _res = args_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_127[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_125[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args"));
     }
     { // expression for_if_clauses
@@ -23220,7 +23143,7 @@ _tmp_127_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_127[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses"));
+        D(fprintf(stderr, "%*c> _tmp_125[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses"));
         expr_ty expression_var;
         asdl_seq* for_if_clauses_var;
         if (
@@ -23229,12 +23152,12 @@ _tmp_127_rule(Parser *p)
             (for_if_clauses_var = for_if_clauses_rule(p))  // for_if_clauses
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_127[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses"));
+            D(fprintf(stderr, "%*c+ _tmp_125[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses"));
             _res = _PyPegen_dummy_name(p, expression_var, for_if_clauses_var);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_127[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_125[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression for_if_clauses"));
     }
     _res = NULL;
@@ -23243,9 +23166,9 @@ _tmp_127_rule(Parser *p)
     return _res;
 }
 
-// _loop0_128: star_named_expressions
+// _loop0_126: star_named_expressions
 static asdl_seq *
-_loop0_128_rule(Parser *p)
+_loop0_126_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -23269,7 +23192,7 @@ _loop0_128_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_128[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expressions"));
+        D(fprintf(stderr, "%*c> _loop0_126[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expressions"));
         asdl_seq* star_named_expressions_var;
         while (
             (star_named_expressions_var = star_named_expressions_rule(p))  // star_named_expressions
@@ -23291,7 +23214,7 @@ _loop0_128_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_128[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_126[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_named_expressions"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -23304,14 +23227,14 @@ _loop0_128_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_128_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_126_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop0_129: (star_targets '=')
+// _loop0_127: (star_targets '=')
 static asdl_seq *
-_loop0_129_rule(Parser *p)
+_loop0_127_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -23335,13 +23258,13 @@ _loop0_129_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_129[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')"));
-        void *_tmp_151_var;
+        D(fprintf(stderr, "%*c> _loop0_127[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')"));
+        void *_tmp_149_var;
         while (
-            (_tmp_151_var = _tmp_151_rule(p))  // star_targets '='
+            (_tmp_149_var = _tmp_149_rule(p))  // star_targets '='
         )
         {
-            _res = _tmp_151_var;
+            _res = _tmp_149_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -23357,7 +23280,7 @@ _loop0_129_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_129[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_127[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(star_targets '=')"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -23370,14 +23293,14 @@ _loop0_129_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_129_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_127_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop0_130: (star_targets '=')
+// _loop0_128: (star_targets '=')
 static asdl_seq *
-_loop0_130_rule(Parser *p)
+_loop0_128_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -23401,13 +23324,13 @@ _loop0_130_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_130[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')"));
-        void *_tmp_152_var;
+        D(fprintf(stderr, "%*c> _loop0_128[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')"));
+        void *_tmp_150_var;
         while (
-            (_tmp_152_var = _tmp_152_rule(p))  // star_targets '='
+            (_tmp_150_var = _tmp_150_rule(p))  // star_targets '='
         )
         {
-            _res = _tmp_152_var;
+            _res = _tmp_150_var;
             if (_n == _children_capacity) {
                 _children_capacity *= 2;
                 void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));
@@ -23423,7 +23346,7 @@ _loop0_130_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_130[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_128[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(star_targets '=')"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -23436,14 +23359,14 @@ _loop0_130_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_130_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_128_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _tmp_131: yield_expr | star_expressions
+// _tmp_129: yield_expr | star_expressions
 static void *
-_tmp_131_rule(Parser *p)
+_tmp_129_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -23457,18 +23380,18 @@ _tmp_131_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_131[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "yield_expr"));
+        D(fprintf(stderr, "%*c> _tmp_129[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "yield_expr"));
         expr_ty yield_expr_var;
         if (
             (yield_expr_var = yield_expr_rule(p))  // yield_expr
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_131[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "yield_expr"));
+            D(fprintf(stderr, "%*c+ _tmp_129[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "yield_expr"));
             _res = yield_expr_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_131[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_129[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "yield_expr"));
     }
     { // star_expressions
@@ -23476,18 +23399,18 @@ _tmp_131_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_131[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_expressions"));
+        D(fprintf(stderr, "%*c> _tmp_129[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_expressions"));
         expr_ty star_expressions_var;
         if (
             (star_expressions_var = star_expressions_rule(p))  // star_expressions
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_131[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_expressions"));
+            D(fprintf(stderr, "%*c+ _tmp_129[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_expressions"));
             _res = star_expressions_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_131[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_129[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_expressions"));
     }
     _res = NULL;
@@ -23496,9 +23419,9 @@ _tmp_131_rule(Parser *p)
     return _res;
 }
 
-// _tmp_132: '[' | '(' | '{'
+// _tmp_130: '[' | '(' | '{'
 static void *
-_tmp_132_rule(Parser *p)
+_tmp_130_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -23512,18 +23435,18 @@ _tmp_132_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_132[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'['"));
+        D(fprintf(stderr, "%*c> _tmp_130[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'['"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 9))  // token='['
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_132[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'['"));
+            D(fprintf(stderr, "%*c+ _tmp_130[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'['"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_132[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_130[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'['"));
     }
     { // '('
@@ -23531,18 +23454,18 @@ _tmp_132_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_132[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'('"));
+        D(fprintf(stderr, "%*c> _tmp_130[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'('"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 7))  // token='('
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_132[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'('"));
+            D(fprintf(stderr, "%*c+ _tmp_130[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'('"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_132[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_130[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'('"));
     }
     { // '{'
@@ -23550,18 +23473,18 @@ _tmp_132_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_132[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{'"));
+        D(fprintf(stderr, "%*c> _tmp_130[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 25))  // token='{'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_132[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{'"));
+            D(fprintf(stderr, "%*c+ _tmp_130[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_132[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_130[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{'"));
     }
     _res = NULL;
@@ -23570,9 +23493,9 @@ _tmp_132_rule(Parser *p)
     return _res;
 }
 
-// _loop0_133: param_no_default
+// _loop0_131: param_no_default
 static asdl_seq *
-_loop0_133_rule(Parser *p)
+_loop0_131_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -23596,7 +23519,7 @@ _loop0_133_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_133[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default"));
+        D(fprintf(stderr, "%*c> _loop0_131[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default"));
         arg_ty param_no_default_var;
         while (
             (param_no_default_var = param_no_default_rule(p))  // param_no_default
@@ -23618,7 +23541,7 @@ _loop0_133_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_133[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_131[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_no_default"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -23631,14 +23554,14 @@ _loop0_133_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_133_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_131_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _tmp_134: slash_with_default | param_with_default+
+// _tmp_132: slash_with_default | param_with_default+
 static void *
-_tmp_134_rule(Parser *p)
+_tmp_132_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -23652,18 +23575,18 @@ _tmp_134_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_134[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_with_default"));
+        D(fprintf(stderr, "%*c> _tmp_132[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_with_default"));
         SlashWithDefault* slash_with_default_var;
         if (
             (slash_with_default_var = slash_with_default_rule(p))  // slash_with_default
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_134[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_with_default"));
+            D(fprintf(stderr, "%*c+ _tmp_132[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_with_default"));
             _res = slash_with_default_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_134[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_132[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slash_with_default"));
     }
     { // param_with_default+
@@ -23671,18 +23594,18 @@ _tmp_134_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_134[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_with_default+"));
-        asdl_seq * _loop1_153_var;
+        D(fprintf(stderr, "%*c> _tmp_132[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_with_default+"));
+        asdl_seq * _loop1_151_var;
         if (
-            (_loop1_153_var = _loop1_153_rule(p))  // param_with_default+
+            (_loop1_151_var = _loop1_151_rule(p))  // param_with_default+
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_134[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_with_default+"));
-            _res = _loop1_153_var;
+            D(fprintf(stderr, "%*c+ _tmp_132[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_with_default+"));
+            _res = _loop1_151_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_134[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_132[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_with_default+"));
     }
     _res = NULL;
@@ -23691,9 +23614,9 @@ _tmp_134_rule(Parser *p)
     return _res;
 }
 
-// _loop0_135: lambda_param_no_default
+// _loop0_133: lambda_param_no_default
 static asdl_seq *
-_loop0_135_rule(Parser *p)
+_loop0_133_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -23717,7 +23640,7 @@ _loop0_135_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop0_135[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default"));
+        D(fprintf(stderr, "%*c> _loop0_133[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default"));
         arg_ty lambda_param_no_default_var;
         while (
             (lambda_param_no_default_var = lambda_param_no_default_rule(p))  // lambda_param_no_default
@@ -23739,7 +23662,7 @@ _loop0_135_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop0_135[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop0_133[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default"));
     }
     asdl_seq *_seq = _Py_asdl_seq_new(_n, p->arena);
@@ -23752,14 +23675,14 @@ _loop0_135_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop0_135_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop0_133_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _tmp_136: lambda_slash_with_default | lambda_param_with_default+
+// _tmp_134: lambda_slash_with_default | lambda_param_with_default+
 static void *
-_tmp_136_rule(Parser *p)
+_tmp_134_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -23773,18 +23696,18 @@ _tmp_136_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_136[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default"));
+        D(fprintf(stderr, "%*c> _tmp_134[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default"));
         SlashWithDefault* lambda_slash_with_default_var;
         if (
             (lambda_slash_with_default_var = lambda_slash_with_default_rule(p))  // lambda_slash_with_default
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_136[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default"));
+            D(fprintf(stderr, "%*c+ _tmp_134[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default"));
             _res = lambda_slash_with_default_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_136[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_134[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_slash_with_default"));
     }
     { // lambda_param_with_default+
@@ -23792,18 +23715,18 @@ _tmp_136_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_136[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default+"));
-        asdl_seq * _loop1_154_var;
+        D(fprintf(stderr, "%*c> _tmp_134[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default+"));
+        asdl_seq * _loop1_152_var;
         if (
-            (_loop1_154_var = _loop1_154_rule(p))  // lambda_param_with_default+
+            (_loop1_152_var = _loop1_152_rule(p))  // lambda_param_with_default+
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_136[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default+"));
-            _res = _loop1_154_var;
+            D(fprintf(stderr, "%*c+ _tmp_134[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default+"));
+            _res = _loop1_152_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_136[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_134[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_with_default+"));
     }
     _res = NULL;
@@ -23812,9 +23735,9 @@ _tmp_136_rule(Parser *p)
     return _res;
 }
 
-// _tmp_137: ')' | ',' (')' | '**')
+// _tmp_135: ')' | ',' (')' | '**')
 static void *
-_tmp_137_rule(Parser *p)
+_tmp_135_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -23828,18 +23751,18 @@ _tmp_137_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_137[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'"));
+        D(fprintf(stderr, "%*c> _tmp_135[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 8))  // token=')'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_137[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'"));
+            D(fprintf(stderr, "%*c+ _tmp_135[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_137[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_135[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'"));
     }
     { // ',' (')' | '**')
@@ -23847,21 +23770,21 @@ _tmp_137_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_137[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')"));
+        D(fprintf(stderr, "%*c> _tmp_135[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')"));
         Token * _literal;
-        void *_tmp_155_var;
+        void *_tmp_153_var;
         if (
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
             &&
-            (_tmp_155_var = _tmp_155_rule(p))  // ')' | '**'
+            (_tmp_153_var = _tmp_153_rule(p))  // ')' | '**'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_137[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')"));
-            _res = _PyPegen_dummy_name(p, _literal, _tmp_155_var);
+            D(fprintf(stderr, "%*c+ _tmp_135[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')"));
+            _res = _PyPegen_dummy_name(p, _literal, _tmp_153_var);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_137[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_135[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (')' | '**')"));
     }
     _res = NULL;
@@ -23870,9 +23793,9 @@ _tmp_137_rule(Parser *p)
     return _res;
 }
 
-// _tmp_138: ':' | ',' (':' | '**')
+// _tmp_136: ':' | ',' (':' | '**')
 static void *
-_tmp_138_rule(Parser *p)
+_tmp_136_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -23886,18 +23809,18 @@ _tmp_138_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_138[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'"));
+        D(fprintf(stderr, "%*c> _tmp_136[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 11))  // token=':'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_138[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'"));
+            D(fprintf(stderr, "%*c+ _tmp_136[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_138[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_136[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'"));
     }
     { // ',' (':' | '**')
@@ -23905,21 +23828,21 @@ _tmp_138_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_138[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')"));
+        D(fprintf(stderr, "%*c> _tmp_136[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')"));
         Token * _literal;
-        void *_tmp_156_var;
+        void *_tmp_154_var;
         if (
             (_literal = _PyPegen_expect_token(p, 12))  // token=','
             &&
-            (_tmp_156_var = _tmp_156_rule(p))  // ':' | '**'
+            (_tmp_154_var = _tmp_154_rule(p))  // ':' | '**'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_138[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')"));
-            _res = _PyPegen_dummy_name(p, _literal, _tmp_156_var);
+            D(fprintf(stderr, "%*c+ _tmp_136[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')"));
+            _res = _PyPegen_dummy_name(p, _literal, _tmp_154_var);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_138[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_136[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (':' | '**')"));
     }
     _res = NULL;
@@ -23928,9 +23851,9 @@ _tmp_138_rule(Parser *p)
     return _res;
 }
 
-// _tmp_139: star_targets '='
+// _tmp_137: star_targets '='
 static void *
-_tmp_139_rule(Parser *p)
+_tmp_137_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -23944,7 +23867,7 @@ _tmp_139_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_139[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='"));
+        D(fprintf(stderr, "%*c> _tmp_137[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='"));
         Token * _literal;
         expr_ty z;
         if (
@@ -23953,7 +23876,7 @@ _tmp_139_rule(Parser *p)
             (_literal = _PyPegen_expect_token(p, 22))  // token='='
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_139[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='"));
+            D(fprintf(stderr, "%*c+ _tmp_137[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='"));
             _res = z;
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -23963,7 +23886,7 @@ _tmp_139_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_139[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_137[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_targets '='"));
     }
     _res = NULL;
@@ -23972,9 +23895,9 @@ _tmp_139_rule(Parser *p)
     return _res;
 }
 
-// _tmp_140: '.' | '...'
+// _tmp_138: '.' | '...'
 static void *
-_tmp_140_rule(Parser *p)
+_tmp_138_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -23988,18 +23911,18 @@ _tmp_140_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_140[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'"));
+        D(fprintf(stderr, "%*c> _tmp_138[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 23))  // token='.'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_140[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'"));
+            D(fprintf(stderr, "%*c+ _tmp_138[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_140[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_138[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'.'"));
     }
     { // '...'
@@ -24007,18 +23930,18 @@ _tmp_140_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_140[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'...'"));
+        D(fprintf(stderr, "%*c> _tmp_138[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'...'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 52))  // token='...'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_140[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'...'"));
+            D(fprintf(stderr, "%*c+ _tmp_138[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'...'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_140[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_138[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'...'"));
     }
     _res = NULL;
@@ -24027,9 +23950,9 @@ _tmp_140_rule(Parser *p)
     return _res;
 }
 
-// _tmp_141: '.' | '...'
+// _tmp_139: '.' | '...'
 static void *
-_tmp_141_rule(Parser *p)
+_tmp_139_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -24043,18 +23966,18 @@ _tmp_141_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_141[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'"));
+        D(fprintf(stderr, "%*c> _tmp_139[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 23))  // token='.'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_141[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'"));
+            D(fprintf(stderr, "%*c+ _tmp_139[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_141[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_139[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'.'"));
     }
     { // '...'
@@ -24062,18 +23985,18 @@ _tmp_141_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_141[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'...'"));
+        D(fprintf(stderr, "%*c> _tmp_139[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'...'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 52))  // token='...'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_141[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'...'"));
+            D(fprintf(stderr, "%*c+ _tmp_139[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'...'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_141[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_139[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'...'"));
     }
     _res = NULL;
@@ -24082,9 +24005,9 @@ _tmp_141_rule(Parser *p)
     return _res;
 }
 
-// _tmp_142: '@' named_expression NEWLINE
+// _tmp_140: '@' named_expression NEWLINE
 static void *
-_tmp_142_rule(Parser *p)
+_tmp_140_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -24098,7 +24021,7 @@ _tmp_142_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_142[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE"));
+        D(fprintf(stderr, "%*c> _tmp_140[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE"));
         Token * _literal;
         expr_ty f;
         Token * newline_var;
@@ -24110,7 +24033,7 @@ _tmp_142_rule(Parser *p)
             (newline_var = _PyPegen_expect_token(p, NEWLINE))  // token='NEWLINE'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_142[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE"));
+            D(fprintf(stderr, "%*c+ _tmp_140[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE"));
             _res = f;
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -24120,7 +24043,7 @@ _tmp_142_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_142[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_140[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'@' named_expression NEWLINE"));
     }
     _res = NULL;
@@ -24129,9 +24052,9 @@ _tmp_142_rule(Parser *p)
     return _res;
 }
 
-// _tmp_143: ',' star_expression
+// _tmp_141: ',' star_expression
 static void *
-_tmp_143_rule(Parser *p)
+_tmp_141_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -24145,7 +24068,7 @@ _tmp_143_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_143[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_expression"));
+        D(fprintf(stderr, "%*c> _tmp_141[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_expression"));
         Token * _literal;
         expr_ty c;
         if (
@@ -24154,7 +24077,7 @@ _tmp_143_rule(Parser *p)
             (c = star_expression_rule(p))  // star_expression
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_143[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_expression"));
+            D(fprintf(stderr, "%*c+ _tmp_141[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_expression"));
             _res = c;
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -24164,7 +24087,7 @@ _tmp_143_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_143[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_141[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_expression"));
     }
     _res = NULL;
@@ -24173,9 +24096,9 @@ _tmp_143_rule(Parser *p)
     return _res;
 }
 
-// _tmp_144: ',' expression
+// _tmp_142: ',' expression
 static void *
-_tmp_144_rule(Parser *p)
+_tmp_142_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -24189,7 +24112,7 @@ _tmp_144_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_144[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' expression"));
+        D(fprintf(stderr, "%*c> _tmp_142[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' expression"));
         Token * _literal;
         expr_ty c;
         if (
@@ -24198,7 +24121,7 @@ _tmp_144_rule(Parser *p)
             (c = expression_rule(p))  // expression
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_144[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' expression"));
+            D(fprintf(stderr, "%*c+ _tmp_142[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' expression"));
             _res = c;
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -24208,7 +24131,7 @@ _tmp_144_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_144[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_142[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' expression"));
     }
     _res = NULL;
@@ -24217,9 +24140,9 @@ _tmp_144_rule(Parser *p)
     return _res;
 }
 
-// _tmp_145: 'or' conjunction
+// _tmp_143: 'or' conjunction
 static void *
-_tmp_145_rule(Parser *p)
+_tmp_143_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -24233,7 +24156,7 @@ _tmp_145_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_145[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'or' conjunction"));
+        D(fprintf(stderr, "%*c> _tmp_143[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'or' conjunction"));
         Token * _keyword;
         expr_ty c;
         if (
@@ -24242,7 +24165,7 @@ _tmp_145_rule(Parser *p)
             (c = conjunction_rule(p))  // conjunction
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_145[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'or' conjunction"));
+            D(fprintf(stderr, "%*c+ _tmp_143[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'or' conjunction"));
             _res = c;
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -24252,7 +24175,7 @@ _tmp_145_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_145[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_143[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'or' conjunction"));
     }
     _res = NULL;
@@ -24261,9 +24184,9 @@ _tmp_145_rule(Parser *p)
     return _res;
 }
 
-// _tmp_146: 'and' inversion
+// _tmp_144: 'and' inversion
 static void *
-_tmp_146_rule(Parser *p)
+_tmp_144_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -24277,7 +24200,7 @@ _tmp_146_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_146[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'and' inversion"));
+        D(fprintf(stderr, "%*c> _tmp_144[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'and' inversion"));
         Token * _keyword;
         expr_ty c;
         if (
@@ -24286,7 +24209,7 @@ _tmp_146_rule(Parser *p)
             (c = inversion_rule(p))  // inversion
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_146[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'and' inversion"));
+            D(fprintf(stderr, "%*c+ _tmp_144[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'and' inversion"));
             _res = c;
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -24296,7 +24219,7 @@ _tmp_146_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_146[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_144[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'and' inversion"));
     }
     _res = NULL;
@@ -24305,9 +24228,9 @@ _tmp_146_rule(Parser *p)
     return _res;
 }
 
-// _tmp_147: 'if' disjunction
+// _tmp_145: 'if' disjunction
 static void *
-_tmp_147_rule(Parser *p)
+_tmp_145_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -24321,7 +24244,7 @@ _tmp_147_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_147[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction"));
+        D(fprintf(stderr, "%*c> _tmp_145[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction"));
         Token * _keyword;
         expr_ty z;
         if (
@@ -24330,7 +24253,7 @@ _tmp_147_rule(Parser *p)
             (z = disjunction_rule(p))  // disjunction
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_147[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction"));
+            D(fprintf(stderr, "%*c+ _tmp_145[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction"));
             _res = z;
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -24340,7 +24263,7 @@ _tmp_147_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_147[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_145[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'if' disjunction"));
     }
     _res = NULL;
@@ -24349,9 +24272,9 @@ _tmp_147_rule(Parser *p)
     return _res;
 }
 
-// _tmp_148: 'if' disjunction
+// _tmp_146: 'if' disjunction
 static void *
-_tmp_148_rule(Parser *p)
+_tmp_146_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -24365,7 +24288,7 @@ _tmp_148_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_148[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction"));
+        D(fprintf(stderr, "%*c> _tmp_146[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction"));
         Token * _keyword;
         expr_ty z;
         if (
@@ -24374,7 +24297,7 @@ _tmp_148_rule(Parser *p)
             (z = disjunction_rule(p))  // disjunction
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_148[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction"));
+            D(fprintf(stderr, "%*c+ _tmp_146[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction"));
             _res = z;
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -24384,7 +24307,7 @@ _tmp_148_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_148[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_146[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'if' disjunction"));
     }
     _res = NULL;
@@ -24393,9 +24316,9 @@ _tmp_148_rule(Parser *p)
     return _res;
 }
 
-// _tmp_149: starred_expression | named_expression !'='
+// _tmp_147: starred_expression | named_expression !'='
 static void *
-_tmp_149_rule(Parser *p)
+_tmp_147_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -24409,18 +24332,18 @@ _tmp_149_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression"));
+        D(fprintf(stderr, "%*c> _tmp_147[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression"));
         expr_ty starred_expression_var;
         if (
             (starred_expression_var = starred_expression_rule(p))  // starred_expression
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression"));
+            D(fprintf(stderr, "%*c+ _tmp_147[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression"));
             _res = starred_expression_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_147[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression"));
     }
     { // named_expression !'='
@@ -24428,7 +24351,7 @@ _tmp_149_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "named_expression !'='"));
+        D(fprintf(stderr, "%*c> _tmp_147[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "named_expression !'='"));
         expr_ty named_expression_var;
         if (
             (named_expression_var = named_expression_rule(p))  // named_expression
@@ -24436,12 +24359,12 @@ _tmp_149_rule(Parser *p)
             _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 22)  // token='='
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "named_expression !'='"));
+            D(fprintf(stderr, "%*c+ _tmp_147[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "named_expression !'='"));
             _res = named_expression_var;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_147[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "named_expression !'='"));
     }
     _res = NULL;
@@ -24450,9 +24373,9 @@ _tmp_149_rule(Parser *p)
     return _res;
 }
 
-// _tmp_150: ',' star_target
+// _tmp_148: ',' star_target
 static void *
-_tmp_150_rule(Parser *p)
+_tmp_148_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -24466,7 +24389,7 @@ _tmp_150_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target"));
+        D(fprintf(stderr, "%*c> _tmp_148[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target"));
         Token * _literal;
         expr_ty c;
         if (
@@ -24475,7 +24398,7 @@ _tmp_150_rule(Parser *p)
             (c = star_target_rule(p))  // star_target
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_target"));
+            D(fprintf(stderr, "%*c+ _tmp_148[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_target"));
             _res = c;
             if (_res == NULL && PyErr_Occurred()) {
                 p->error_indicator = 1;
@@ -24485,7 +24408,7 @@ _tmp_150_rule(Parser *p)
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_148[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_target"));
     }
     _res = NULL;
@@ -24494,9 +24417,9 @@ _tmp_150_rule(Parser *p)
     return _res;
 }
 
-// _tmp_151: star_targets '='
+// _tmp_149: star_targets '='
 static void *
-_tmp_151_rule(Parser *p)
+_tmp_149_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -24510,7 +24433,7 @@ _tmp_151_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='"));
+        D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='"));
         Token * _literal;
         expr_ty star_targets_var;
         if (
@@ -24519,12 +24442,12 @@ _tmp_151_rule(Parser *p)
             (_literal = _PyPegen_expect_token(p, 22))  // token='='
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='"));
+            D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='"));
             _res = _PyPegen_dummy_name(p, star_targets_var, _literal);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_targets '='"));
     }
     _res = NULL;
@@ -24533,9 +24456,9 @@ _tmp_151_rule(Parser *p)
     return _res;
 }
 
-// _tmp_152: star_targets '='
+// _tmp_150: star_targets '='
 static void *
-_tmp_152_rule(Parser *p)
+_tmp_150_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -24549,7 +24472,7 @@ _tmp_152_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='"));
+        D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='"));
         Token * _literal;
         expr_ty star_targets_var;
         if (
@@ -24558,12 +24481,12 @@ _tmp_152_rule(Parser *p)
             (_literal = _PyPegen_expect_token(p, 22))  // token='='
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_152[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='"));
+            D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='"));
             _res = _PyPegen_dummy_name(p, star_targets_var, _literal);
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_152[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_targets '='"));
     }
     _res = NULL;
@@ -24572,9 +24495,9 @@ _tmp_152_rule(Parser *p)
     return _res;
 }
 
-// _loop1_153: param_with_default
+// _loop1_151: param_with_default
 static asdl_seq *
-_loop1_153_rule(Parser *p)
+_loop1_151_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -24598,7 +24521,7 @@ _loop1_153_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_with_default"));
+        D(fprintf(stderr, "%*c> _loop1_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_with_default"));
         NameDefaultPair* param_with_default_var;
         while (
             (param_with_default_var = param_with_default_rule(p))  // param_with_default
@@ -24620,7 +24543,7 @@ _loop1_153_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_153[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_151[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_with_default"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -24638,14 +24561,14 @@ _loop1_153_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop1_153_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop1_151_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _loop1_154: lambda_param_with_default
+// _loop1_152: lambda_param_with_default
 static asdl_seq *
-_loop1_154_rule(Parser *p)
+_loop1_152_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -24669,7 +24592,7 @@ _loop1_154_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _loop1_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default"));
+        D(fprintf(stderr, "%*c> _loop1_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default"));
         NameDefaultPair* lambda_param_with_default_var;
         while (
             (lambda_param_with_default_var = lambda_param_with_default_rule(p))  // lambda_param_with_default
@@ -24691,7 +24614,7 @@ _loop1_154_rule(Parser *p)
             _mark = p->mark;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _loop1_154[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _loop1_152[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_with_default"));
     }
     if (_n == 0 || p->error_indicator) {
@@ -24709,14 +24632,14 @@ _loop1_154_rule(Parser *p)
     }
     for (int i = 0; i < _n; i++) asdl_seq_SET(_seq, i, _children[i]);
     PyMem_Free(_children);
-    _PyPegen_insert_memo(p, _start_mark, _loop1_154_type, _seq);
+    _PyPegen_insert_memo(p, _start_mark, _loop1_152_type, _seq);
     D(p->level--);
     return _seq;
 }
 
-// _tmp_155: ')' | '**'
+// _tmp_153: ')' | '**'
 static void *
-_tmp_155_rule(Parser *p)
+_tmp_153_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -24730,18 +24653,18 @@ _tmp_155_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'"));
+        D(fprintf(stderr, "%*c> _tmp_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 8))  // token=')'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_155[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'"));
+            D(fprintf(stderr, "%*c+ _tmp_153[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_155[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_153[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'"));
     }
     { // '**'
@@ -24749,18 +24672,18 @@ _tmp_155_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'"));
+        D(fprintf(stderr, "%*c> _tmp_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 35))  // token='**'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_155[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'"));
+            D(fprintf(stderr, "%*c+ _tmp_153[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_155[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_153[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**'"));
     }
     _res = NULL;
@@ -24769,9 +24692,9 @@ _tmp_155_rule(Parser *p)
     return _res;
 }
 
-// _tmp_156: ':' | '**'
+// _tmp_154: ':' | '**'
 static void *
-_tmp_156_rule(Parser *p)
+_tmp_154_rule(Parser *p)
 {
     D(p->level++);
     if (p->error_indicator) {
@@ -24785,18 +24708,18 @@ _tmp_156_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_156[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'"));
+        D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 11))  // token=':'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_156[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'"));
+            D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_156[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'"));
     }
     { // '**'
@@ -24804,18 +24727,18 @@ _tmp_156_rule(Parser *p)
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> _tmp_156[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'"));
+        D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'"));
         Token * _literal;
         if (
             (_literal = _PyPegen_expect_token(p, 35))  // token='**'
         )
         {
-            D(fprintf(stderr, "%*c+ _tmp_156[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'"));
+            D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'"));
             _res = _literal;
             goto done;
         }
         p->mark = _mark;
-        D(fprintf(stderr, "%*c%s _tmp_156[%d-%d]: %s failed!\n", p->level, ' ',
+        D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**'"));
     }
     _res = NULL;
index 7b02bdd..fb0c4af 100644 (file)
@@ -74,6 +74,9 @@ decode_unicode_with_escapes(Parser *parser, const char *s, size_t len, Token *t)
         return NULL;
     }
     p = buf = PyBytes_AsString(u);
+    if (p == NULL) {
+        return NULL;
+    }
     end = s + len;
     while (s < end) {
         if (*s == '\\') {
index 2c435fb..4e742a5 100644 (file)
@@ -62,8 +62,7 @@ init_normalization(Parser *p)
 /* Checks if the NOTEQUAL token is valid given the current parser flags
 0 indicates success and nonzero indicates failure (an exception may be set) */
 int
-_PyPegen_check_barry_as_flufl(Parser *p) {
-    Token *t = p->tokens[p->fill - 1];
+_PyPegen_check_barry_as_flufl(Parser *p, Token* t) {
     assert(t->bytes != NULL);
     assert(t->type == NOTEQUAL);
 
@@ -989,7 +988,8 @@ bad_single_statement(Parser *p)
 
     /* Newlines are allowed if preceded by a line continuation character
        or if they appear inside a string. */
-    if (!cur || *(cur - 1) == '\\' || newline_in_string(p, cur)) {
+    if (!cur || (cur != p->tok->buf && *(cur - 1) == '\\')
+             || newline_in_string(p, cur)) {
         return 0;
     }
     char c = *cur;
@@ -1100,15 +1100,28 @@ _PyPegen_Parser_New(struct tok_state *tok, int start_rule, int flags,
     p->feature_version = feature_version;
     p->known_err_token = NULL;
     p->level = 0;
+    p->call_invalid_rules = 0;
 
     return p;
 }
 
+static void
+reset_parser_state(Parser *p)
+{
+    for (int i = 0; i < p->fill; i++) {
+        p->tokens[i]->memo = NULL;
+    }
+    p->mark = 0;
+    p->call_invalid_rules = 1;
+}
+
 void *
 _PyPegen_run_parser(Parser *p)
 {
     void *res = _PyPegen_parse(p);
     if (res == NULL) {
+        reset_parser_state(p);
+        _PyPegen_parse(p);
         if (PyErr_Occurred()) {
             return NULL;
         }
index bd35d4f..a2f524a 100644 (file)
@@ -73,6 +73,7 @@ typedef struct {
     growable_comment_array type_ignore_comments;
     Token *known_err_token;
     int level;
+    int call_invalid_rules;
 } Parser;
 
 typedef struct {
@@ -262,7 +263,7 @@ expr_ty _PyPegen_collect_call_seqs(Parser *, asdl_seq *, asdl_seq *,
                      int end_col_offset, PyArena *arena);
 expr_ty _PyPegen_concatenate_strings(Parser *p, asdl_seq *);
 asdl_seq *_PyPegen_join_sequences(Parser *, asdl_seq *, asdl_seq *);
-int _PyPegen_check_barry_as_flufl(Parser *);
+int _PyPegen_check_barry_as_flufl(Parser *, Token *);
 mod_ty _PyPegen_make_module(Parser *, asdl_seq *);
 
 // Error reporting helpers
index 4d65bb3..91a78fe 100644 (file)
@@ -69,11 +69,14 @@ create_filter(PyObject *category, _Py_Identifier *id, const char *modname)
         }
     } else {
         modname_obj = Py_None;
+        Py_INCREF(modname_obj);
     }
 
     /* This assumes the line number is zero for now. */
-    return PyTuple_Pack(5, action_str, Py_None,
-                        category, modname_obj, _PyLong_Zero);
+    PyObject *filter = PyTuple_Pack(5, action_str, Py_None,
+                                    category, modname_obj, _PyLong_Zero);
+    Py_DECREF(modname_obj);
+    return filter;
 }
 #endif
 
index 4736930..a212f69 100644 (file)
 #  include <sanitizer/msan_interface.h>
 #endif
 
+#if defined(__APPLE__) && defined(__has_builtin) 
+#  if __has_builtin(__builtin_available)
+#    define HAVE_GETENTRYPY_GETRANDOM_RUNTIME __builtin_available(macOS 10.12, iOS 10.10, tvOS 10.0, watchOS 3.0, *)
+#  endif
+#endif
+#ifndef HAVE_GETENTRYPY_GETRANDOM_RUNTIME
+#  define HAVE_GETENTRYPY_GETRANDOM_RUNTIME 1
+#endif
+
+
 #ifdef Py_DEBUG
 int _Py_HashSecret_Initialized = 0;
 #else
@@ -208,6 +218,16 @@ py_getrandom(void *buffer, Py_ssize_t size, int blocking, int raise)
      error.
 
    getentropy() is retried if it failed with EINTR: interrupted by a signal. */
+
+#if defined(__APPLE__) && defined(__has_attribute) && __has_attribute(availability)
+static int
+py_getentropy(char *buffer, Py_ssize_t size, int raise) 
+        __attribute__((availability(macos,introduced=10.12)))
+        __attribute__((availability(ios,introduced=10.0)))
+        __attribute__((availability(tvos,introduced=10.0)))
+        __attribute__((availability(watchos,introduced=3.0)));
+#endif
+
 static int
 py_getentropy(char *buffer, Py_ssize_t size, int raise)
 {
@@ -498,19 +518,21 @@ pyurandom(void *buffer, Py_ssize_t size, int blocking, int raise)
 #else
 
 #if defined(PY_GETRANDOM) || defined(PY_GETENTROPY)
+    if (HAVE_GETENTRYPY_GETRANDOM_RUNTIME) {
 #ifdef PY_GETRANDOM
-    res = py_getrandom(buffer, size, blocking, raise);
+        res = py_getrandom(buffer, size, blocking, raise);
 #else
-    res = py_getentropy(buffer, size, raise);
+        res = py_getentropy(buffer, size, raise);
 #endif
-    if (res < 0) {
-        return -1;
-    }
-    if (res == 1) {
-        return 0;
-    }
-    /* getrandom() or getentropy() function is not available: failed with
-       ENOSYS or EPERM. Fall back on reading from /dev/urandom. */
+        if (res < 0) {
+            return -1;
+        }
+        if (res == 1) {
+            return 0;
+        }
+        /* getrandom() or getentropy() function is not available: failed with
+           ENOSYS or EPERM. Fall back on reading from /dev/urandom. */
+        } /* end of availability block */
 #endif
 
     return dev_urandom(buffer, size, raise);
index 3392cd0..91e879e 100644 (file)
@@ -196,13 +196,18 @@ UNSIGNAL_PENDING_CALLS(PyInterpreterState *interp)
 
 
 static inline void
-SIGNAL_PENDING_SIGNALS(PyInterpreterState *interp)
+SIGNAL_PENDING_SIGNALS(PyInterpreterState *interp, int force)
 {
     struct _ceval_runtime_state *ceval = &interp->runtime->ceval;
     struct _ceval_state *ceval2 = &interp->ceval;
     _Py_atomic_store_relaxed(&ceval->signals_pending, 1);
-    /* eval_breaker is not set to 1 if thread_can_handle_signals() is false */
-    COMPUTE_EVAL_BREAKER(interp, ceval, ceval2);
+    if (force) {
+        _Py_atomic_store_relaxed(&ceval2->eval_breaker, 1);
+    }
+    else {
+        /* eval_breaker is not set to 1 if thread_can_handle_signals() is false */
+        COMPUTE_EVAL_BREAKER(interp, ceval, ceval2);
+    }
 }
 
 
@@ -491,10 +496,22 @@ PyEval_RestoreThread(PyThreadState *tstate)
 void
 _PyEval_SignalReceived(PyInterpreterState *interp)
 {
+#ifdef MS_WINDOWS
+    // bpo-42296: On Windows, _PyEval_SignalReceived() is called from a signal
+    // handler which can run in a thread different than the Python thread, in
+    // which case _Py_ThreadCanHandleSignals() is wrong. Ignore
+    // _Py_ThreadCanHandleSignals() and always set eval_breaker to 1.
+    //
+    // The next eval_frame_handle_pending() call will call
+    // _Py_ThreadCanHandleSignals() to recompute eval_breaker.
+    int force = 1;
+#else
+    int force = 0;
+#endif
     /* bpo-30703: Function called when the C signal handler of Python gets a
        signal. We cannot queue a callback using _PyEval_AddPendingCall() since
        that function is not async-signal-safe. */
-    SIGNAL_PENDING_SIGNALS(interp);
+    SIGNAL_PENDING_SIGNALS(interp, force);
 }
 
 /* Push one item onto the queue while holding the lock. */
@@ -594,7 +611,7 @@ handle_signals(PyThreadState *tstate)
     UNSIGNAL_PENDING_SIGNALS(tstate->interp);
     if (_PyErr_CheckSignalsTstate(tstate) < 0) {
         /* On failure, re-schedule a call to handle_signals(). */
-        SIGNAL_PENDING_SIGNALS(tstate->interp);
+        SIGNAL_PENDING_SIGNALS(tstate->interp, 0);
         return -1;
     }
     return 0;
@@ -883,6 +900,17 @@ eval_frame_handle_pending(PyThreadState *tstate)
         return -1;
     }
 
+#ifdef MS_WINDOWS
+    // bpo-42296: On Windows, _PyEval_SignalReceived() can be called in a
+    // different thread than the Python thread, in which case
+    // _Py_ThreadCanHandleSignals() is wrong. Recompute eval_breaker in the
+    // current Python thread with the correct _Py_ThreadCanHandleSignals()
+    // value. It prevents to interrupt the eval loop at every instruction if
+    // the current Python thread cannot handle signals (if
+    // _Py_ThreadCanHandleSignals() is false).
+    COMPUTE_EVAL_BREAKER(tstate->interp, ceval, ceval2);
+#endif
+
     return 0;
 }
 
index 51af28b..722d52d 100644 (file)
@@ -84,8 +84,8 @@ It's called a frame block to distinguish it from a basic block in the
 compiler IR.
 */
 
-enum fblocktype { WHILE_LOOP, FOR_LOOP, EXCEPT, FINALLY_TRY, FINALLY_END,
-                  WITH, ASYNC_WITH, HANDLER_CLEANUP, POP_VALUE };
+enum fblocktype { WHILE_LOOP, FOR_LOOP, TRY_EXCEPT, FINALLY_TRY, FINALLY_END,
+                  WITH, ASYNC_WITH, HANDLER_CLEANUP, POP_VALUE, EXCEPTION_HANDLER };
 
 struct fblockinfo {
     enum fblocktype fb_type;
@@ -1624,9 +1624,7 @@ compiler_push_fblock(struct compiler *c, enum fblocktype t, basicblock *b,
 {
     struct fblockinfo *f;
     if (c->u->u_nfblocks >= CO_MAXBLOCKS) {
-        PyErr_SetString(PyExc_SyntaxError,
-                        "too many statically nested blocks");
-        return 0;
+        return compiler_error(c, "too many statically nested blocks");
     }
     f = &c->u->u_fblock[c->u->u_nfblocks++];
     f->fb_type = t;
@@ -1666,6 +1664,7 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
 {
     switch (info->fb_type) {
         case WHILE_LOOP:
+        case EXCEPTION_HANDLER:
             return 1;
 
         case FOR_LOOP:
@@ -1676,7 +1675,7 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
             ADDOP(c, POP_TOP);
             return 1;
 
-        case EXCEPT:
+        case TRY_EXCEPT:
             ADDOP(c, POP_BLOCK);
             return 1;
 
@@ -3064,14 +3063,17 @@ compiler_try_except(struct compiler *c, stmt_ty s)
         return 0;
     ADDOP_JREL(c, SETUP_FINALLY, except);
     compiler_use_next_block(c, body);
-    if (!compiler_push_fblock(c, EXCEPT, body, NULL, NULL))
+    if (!compiler_push_fblock(c, TRY_EXCEPT, body, NULL, NULL))
         return 0;
     VISIT_SEQ(c, stmt, s->v.Try.body);
     ADDOP(c, POP_BLOCK);
-    compiler_pop_fblock(c, EXCEPT, body);
+    compiler_pop_fblock(c, TRY_EXCEPT, body);
     ADDOP_JREL(c, JUMP_FORWARD, orelse);
     n = asdl_seq_LEN(s->v.Try.handlers);
     compiler_use_next_block(c, except);
+    /* Runtime will push a block here, so we need to account for that */
+    if (!compiler_push_fblock(c, EXCEPTION_HANDLER, NULL, NULL, NULL))
+        return 0;
     for (i = 0; i < n; i++) {
         excepthandler_ty handler = (excepthandler_ty)asdl_seq_GET(
             s->v.Try.handlers, i);
@@ -3156,6 +3158,7 @@ compiler_try_except(struct compiler *c, stmt_ty s)
         }
         compiler_use_next_block(c, except);
     }
+    compiler_pop_fblock(c, EXCEPTION_HANDLER, NULL);
     ADDOP(c, RERAISE);
     compiler_use_next_block(c, orelse);
     VISIT_SEQ(c, stmt, s->v.Try.orelse);
index 684f10a..97f7698 100644 (file)
@@ -144,10 +144,16 @@ aix_loaderror(const char *pathname)
         ERRBUF_APPEND(message[i]);
         ERRBUF_APPEND("\n");
     }
-    errbuf[strlen(errbuf)-1] = '\0';            /* trim off last newline */
-    pathname_ob = PyUnicode_FromString(pathname);
-    errbuf_ob = PyUnicode_FromString(errbuf);
-    PyErr_SetImportError(errbuf_ob, NULL, pathname);
+    /* Subtract 1 from the length to trim off trailing newline */
+    errbuf_ob = PyUnicode_DecodeLocaleAndSize(errbuf, strlen(errbuf)-1, "surrogateescape");
+    if (errbuf_ob == NULL)
+        return;
+    pathname_ob = PyUnicode_DecodeFSDefault(pathname);
+    if (pathname_ob == NULL) {
+        Py_DECREF(errbuf_ob);
+        return;
+    }
+    PyErr_SetImportError(errbuf_ob, NULL, pathname_ob);
     Py_DECREF(pathname_ob);
     Py_DECREF(errbuf_ob);
     return;
index 4b964a6..e36d608 100644 (file)
@@ -36,9 +36,20 @@ dl_funcptr _PyImport_FindSharedFuncptr(const char *prefix,
         char buf[256];
         PyOS_snprintf(buf, sizeof(buf), "Failed to load %.200s",
                       pathname);
-        PyObject *buf_ob = PyUnicode_FromString(buf);
+        PyObject *buf_ob = PyUnicode_DecodeFSDefault(buf);
+        if (buf_ob == NULL)
+            return NULL;
         PyObject *shortname_ob = PyUnicode_FromString(shortname);
-        PyObject *pathname_ob = PyUnicode_FromString(pathname);
+        if (shortname_ob == NULL) {
+            Py_DECREF(buf_ob);
+            return NULL;
+        }
+        PyObject *pathname_ob = PyUnicode_DecodeFSDefault(pathname);
+        if (pathname_ob == NULL) {
+            Py_DECREF(buf_ob);
+            Py_DECREF(shortname_ob);
+            return NULL;
+        }
         PyErr_SetImportError(buf_ob, shortname_ob, pathname_ob);
         Py_DECREF(buf_ob);
         Py_DECREF(shortname_ob);
index 082154d..2382889 100644 (file)
@@ -106,7 +106,7 @@ _PyImport_FindSharedFuncptr(const char *prefix,
         const char *error = dlerror();
         if (error == NULL)
             error = "unknown dlopen() error";
-        error_ob = PyUnicode_FromString(error);
+        error_ob = PyUnicode_DecodeLocale(error, "surrogateescape");
         if (error_ob == NULL)
             return NULL;
         mod_name = PyUnicode_FromString(shortname);
@@ -114,7 +114,7 @@ _PyImport_FindSharedFuncptr(const char *prefix,
             Py_DECREF(error_ob);
             return NULL;
         }
-        path = PyUnicode_FromString(pathname);
+        path = PyUnicode_DecodeFSDefault(pathname);
         if (path == NULL) {
             Py_DECREF(error_ob);
             Py_DECREF(mod_name);
index 2c86828..397ac34 100644 (file)
@@ -2032,6 +2032,7 @@ _Py_GetLocaleconvNumeric(struct lconv *lc,
     assert(decimal_point != NULL);
     assert(thousands_sep != NULL);
 
+#ifndef MS_WINDOWS
     int change_locale = 0;
     if ((strlen(lc->decimal_point) > 1 || ((unsigned char)lc->decimal_point[0]) > 127)) {
         change_locale = 1;
@@ -2070,14 +2071,20 @@ _Py_GetLocaleconvNumeric(struct lconv *lc,
         }
     }
 
+#define GET_LOCALE_STRING(ATTR) PyUnicode_DecodeLocale(lc->ATTR, NULL)
+#else /* MS_WINDOWS */
+/* Use _W_* fields of Windows strcut lconv */
+#define GET_LOCALE_STRING(ATTR) PyUnicode_FromWideChar(lc->_W_ ## ATTR, -1)
+#endif /* MS_WINDOWS */
+
     int res = -1;
 
-    *decimal_point = PyUnicode_DecodeLocale(lc->decimal_point, NULL);
+    *decimal_point = GET_LOCALE_STRING(decimal_point);
     if (*decimal_point == NULL) {
         goto done;
     }
 
-    *thousands_sep = PyUnicode_DecodeLocale(lc->thousands_sep, NULL);
+    *thousands_sep = GET_LOCALE_STRING(thousands_sep);
     if (*thousands_sep == NULL) {
         goto done;
     }
@@ -2085,9 +2092,13 @@ _Py_GetLocaleconvNumeric(struct lconv *lc,
     res = 0;
 
 done:
+#ifndef MS_WINDOWS
     if (loc != NULL) {
         setlocale(LC_CTYPE, oldloc);
     }
     PyMem_Free(oldloc);
+#endif
     return res;
+
+#undef GET_LOCALE_STRING
 }
index 0e2e7c3..5e39a2f 100644 (file)
@@ -905,7 +905,11 @@ PyImport_AddModule(const char *name)
 }
 
 
-/* Remove name from sys.modules, if it's there. */
+/* Remove name from sys.modules, if it's there.
+ * Can be called with an exception raised.
+ * If fail to remove name a new exception will be chained with the old
+ * exception, otherwise the old exception is preserved.
+ */
 static void
 remove_module(PyThreadState *tstate, PyObject *name)
 {
@@ -913,18 +917,17 @@ remove_module(PyThreadState *tstate, PyObject *name)
     _PyErr_Fetch(tstate, &type, &value, &traceback);
 
     PyObject *modules = tstate->interp->modules;
-    if (!PyMapping_HasKey(modules, name)) {
-        goto out;
+    if (PyDict_CheckExact(modules)) {
+        PyObject *mod = _PyDict_Pop(modules, name, Py_None);
+        Py_XDECREF(mod);
     }
-    if (PyMapping_DelItem(modules, name) < 0) {
-        _PyErr_SetString(tstate, PyExc_RuntimeError,
-                         "deleting key in sys.modules failed");
-        _PyErr_ChainExceptions(type, value, traceback);
-        return;
+    else if (PyMapping_DelItem(modules, name) < 0) {
+        if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
+            _PyErr_Clear(tstate);
+        }
     }
 
-out:
-    _PyErr_Restore(tstate, type, value, traceback);
+    _PyErr_ChainExceptions(type, value, traceback);
 }
 
 
index e882a1f..3caed38 100644 (file)
@@ -42,7 +42,8 @@ Options and arguments (and corresponding environment variables):\n\
          and comparing bytes/bytearray with str. (-bb: issue errors)\n\
 -B     : don't write .pyc files on import; also PYTHONDONTWRITEBYTECODE=x\n\
 -c cmd : program passed in as string (terminates option list)\n\
--d     : debug output from parser; also PYTHONDEBUG=x\n\
+-d     : turn on parser debugging output (for experts only, only works on\n\
+         debug builds); also PYTHONDEBUG=x\n\
 -E     : ignore PYTHON* environment variables (such as PYTHONPATH)\n\
 -h     : print this help message and exit (also --help)\n\
 ";
index 84de1ab..6954c87 100644 (file)
@@ -12,7 +12,7 @@
 
 #define UNCONDITIONAL_JUMP(op)  (op==JUMP_ABSOLUTE || op==JUMP_FORWARD)
 #define CONDITIONAL_JUMP(op) (op==POP_JUMP_IF_FALSE || op==POP_JUMP_IF_TRUE \
-    || op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP || op==JUMP_IF_NOT_EXC_MATCH)
+    || op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP)
 #define ABSOLUTE_JUMP(op) (op==JUMP_ABSOLUTE \
     || op==POP_JUMP_IF_FALSE || op==POP_JUMP_IF_TRUE \
     || op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP || op==JUMP_IF_NOT_EXC_MATCH)
index cfb3a7d..60f091c 100644 (file)
@@ -57,7 +57,6 @@ static PyStatus add_main_module(PyInterpreterState *interp);
 static PyStatus init_import_site(void);
 static PyStatus init_set_builtins_open(void);
 static PyStatus init_sys_streams(PyThreadState *tstate);
-static PyStatus init_signals(PyThreadState *tstate);
 static void call_py_exitfuncs(PyThreadState *tstate);
 static void wait_for_thread_shutdown(PyThreadState *tstate);
 static void call_ll_exitfuncs(_PyRuntimeState *runtime);
@@ -1013,11 +1012,8 @@ init_interp_main(PyThreadState *tstate)
     }
 
     if (is_main_interp) {
-        if (config->install_signal_handlers) {
-            status = init_signals(tstate);
-            if (_PyStatus_EXCEPTION(status)) {
-                return status;
-            }
+        if (_PySignal_Init(config->install_signal_handlers) < 0) {
+            return _PyStatus_ERR("can't initialize signals");
         }
 
         if (_PyTraceMalloc_Init(config->tracemalloc) < 0) {
@@ -1357,7 +1353,6 @@ Py_FinalizeEx(void)
 
     /* Get current thread state and interpreter pointer */
     PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
-    PyInterpreterState *interp = tstate->interp;
 
     // Wrap up existing "threading"-module-created, non-daemon threads.
     wait_for_thread_shutdown(tstate);
@@ -1380,13 +1375,13 @@ Py_FinalizeEx(void)
     /* Copy the core config, PyInterpreterState_Delete() free
        the core config memory */
 #ifdef Py_REF_DEBUG
-    int show_ref_count = interp->config.show_ref_count;
+    int show_ref_count = tstate->interp->config.show_ref_count;
 #endif
 #ifdef Py_TRACE_REFS
-    int dump_refs = interp->config.dump_refs;
+    int dump_refs = tstate->interp->config.dump_refs;
 #endif
 #ifdef WITH_PYMALLOC
-    int malloc_stats = interp->config.malloc_stats;
+    int malloc_stats = tstate->interp->config.malloc_stats;
 #endif
 
     /* Remaining daemon threads will automatically exit
@@ -2442,25 +2437,6 @@ Py_Exit(int sts)
     exit(sts);
 }
 
-static PyStatus
-init_signals(PyThreadState *tstate)
-{
-#ifdef SIGPIPE
-    PyOS_setsig(SIGPIPE, SIG_IGN);
-#endif
-#ifdef SIGXFZ
-    PyOS_setsig(SIGXFZ, SIG_IGN);
-#endif
-#ifdef SIGXFSZ
-    PyOS_setsig(SIGXFSZ, SIG_IGN);
-#endif
-    PyOS_InitInterrupts(); /* May imply init_signals() */
-    if (_PyErr_Occurred(tstate)) {
-        return _PyStatus_ERR("can't import signal");
-    }
-    return _PyStatus_OK();
-}
-
 
 /* Restore signals that the interpreter has called SIG_IGN on to SIG_DFL.
  *
index b121b43..89d63e0 100644 (file)
@@ -5,6 +5,12 @@
 
 #if defined(__APPLE__)
 #include <mach/mach_time.h>   /* mach_absolute_time(), mach_timebase_info() */
+
+#if defined(__APPLE__) && defined(__has_builtin) 
+#  if __has_builtin(__builtin_available)
+#    define HAVE_CLOCK_GETTIME_RUNTIME __builtin_available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
+#  endif
+#endif
 #endif
 
 #define _PyTime_check_mul_overflow(a, b) \
@@ -683,15 +689,22 @@ pygettimeofday(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
 
 #else   /* MS_WINDOWS */
     int err;
-#ifdef HAVE_CLOCK_GETTIME
+#if defined(HAVE_CLOCK_GETTIME)
     struct timespec ts;
-#else
+#endif
+
+#if !defined(HAVE_CLOCK_GETTIME) || defined(__APPLE__)
     struct timeval tv;
 #endif
 
     assert(info == NULL || raise);
 
 #ifdef HAVE_CLOCK_GETTIME
+
+#ifdef HAVE_CLOCK_GETTIME_RUNTIME
+    if (HAVE_CLOCK_GETTIME_RUNTIME) {
+#endif
+
     err = clock_gettime(CLOCK_REALTIME, &ts);
     if (err) {
         if (raise) {
@@ -715,7 +728,14 @@ pygettimeofday(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
             info->resolution = 1e-9;
         }
     }
-#else   /* HAVE_CLOCK_GETTIME */
+
+#ifdef HAVE_CLOCK_GETTIME_RUNTIME
+    } else { 
+#endif
+
+#endif
+
+#if !defined(HAVE_CLOCK_GETTIME) || defined(HAVE_CLOCK_GETTIME_RUNTIME)
 
      /* test gettimeofday() */
     err = gettimeofday(&tv, (struct timezone *)NULL);
@@ -735,6 +755,11 @@ pygettimeofday(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
         info->monotonic = 0;
         info->adjustable = 1;
     }
+
+#if defined(HAVE_CLOCK_GETTIME_RUNTIME) && defined(HAVE_CLOCK_GETTIME)
+    } /* end of availibity block */
+#endif
+
 #endif   /* !HAVE_CLOCK_GETTIME */
 #endif   /* !MS_WINDOWS */
     return 0;
index 6488513..a9ab260 100644 (file)
@@ -1,4 +1,4 @@
-This is Python version 3.9.0
+This is Python version 3.9.1
 ============================
 
 .. image:: https://travis-ci.org/python/cpython.svg?branch=3.9
index f630afe..a8a0447 100644 (file)
@@ -60,7 +60,7 @@ def glob_tree(root, *,
 
 
 def iter_files(root, suffix=None, relparent=None, *,
-               get_files=os.walk,
+               get_files=None,
                _glob=glob_tree,
                _walk=walk_tree,
                ):
@@ -75,6 +75,8 @@ def iter_files(root, suffix=None, relparent=None, *,
     if "relparent" is provided then it is used to resolve each
     filename as a relative path.
     """
+    if get_files is None:
+        get_files = os.walk
     if not isinstance(root, str):
         roots = root
         for root in roots:
index b07ffdd..34b5807 100755 (executable)
@@ -1777,6 +1777,30 @@ legacy_converters = {}
 # The callable should not call builtins.print.
 return_converters = {}
 
+
+def write_file(filename, new_contents):
+    try:
+        with open(filename, 'r', encoding="utf-8") as fp:
+            old_contents = fp.read()
+
+        if old_contents == new_contents:
+            # no change: avoid modifying the file modification time
+            return
+    except FileNotFoundError:
+        pass
+
+    # Atomic write using a temporary file and os.replace()
+    filename_new = f"{filename}.new"
+    with open(filename_new, "w", encoding="utf-8") as fp:
+        fp.write(new_contents)
+
+    try:
+        os.replace(filename_new, filename)
+    except:
+        os.unlink(filename_new)
+        raise
+
+
 clinic = None
 class Clinic:
 
@@ -1823,7 +1847,7 @@ impl_definition block
 
 """
 
-    def __init__(self, language, printer=None, *, force=False, verify=True, filename=None):
+    def __init__(self, language, printer=None, *, verify=True, filename=None):
         # maps strings to Parser objects.
         # (instantiated from the "parsers" global.)
         self.parsers = {}
@@ -1832,7 +1856,6 @@ impl_definition block
             fail("Custom printers are broken right now")
         self.printer = printer or BlockPrinter(language)
         self.verify = verify
-        self.force = force
         self.filename = filename
         self.modules = collections.OrderedDict()
         self.classes = collections.OrderedDict()
@@ -1965,8 +1988,7 @@ impl_definition block
                     block.input = 'preserve\n'
                     printer_2 = BlockPrinter(self.language)
                     printer_2.print_block(block)
-                    with open(destination.filename, "wt") as f:
-                        f.write(printer_2.f.getvalue())
+                    write_file(destination.filename, printer_2.f.getvalue())
                     continue
         text = printer.f.getvalue()
 
@@ -2018,7 +2040,10 @@ impl_definition block
         return module, cls
 
 
-def parse_file(filename, *, force=False, verify=True, output=None, encoding='utf-8'):
+def parse_file(filename, *, verify=True, output=None):
+    if not output:
+        output = filename
+
     extension = os.path.splitext(filename)[1][1:]
     if not extension:
         fail("Can't extract file type for file " + repr(filename))
@@ -2028,7 +2053,7 @@ def parse_file(filename, *, force=False, verify=True, output=None, encoding='utf
     except KeyError:
         fail("Can't identify file type for file " + repr(filename))
 
-    with open(filename, 'r', encoding=encoding) as f:
+    with open(filename, 'r', encoding="utf-8") as f:
         raw = f.read()
 
     # exit quickly if there are no clinic markers in the file
@@ -2036,19 +2061,10 @@ def parse_file(filename, *, force=False, verify=True, output=None, encoding='utf
     if not find_start_re.search(raw):
         return
 
-    clinic = Clinic(language, force=force, verify=verify, filename=filename)
+    clinic = Clinic(language, verify=verify, filename=filename)
     cooked = clinic.parse(raw)
-    if (cooked == raw) and not force:
-        return
-
-    directory = os.path.dirname(filename) or '.'
 
-    with tempfile.TemporaryDirectory(prefix="clinic", dir=directory) as tmpdir:
-        bytes = cooked.encode(encoding)
-        tmpfilename = os.path.join(tmpdir, os.path.basename(filename))
-        with open(tmpfilename, "wb") as f:
-            f.write(bytes)
-        os.replace(tmpfilename, output or filename)
+    write_file(output, cooked)
 
 
 def compute_checksum(input, length=None):
@@ -5087,7 +5103,7 @@ For more information see https://docs.python.org/3/howto/clinic.html""")
                 path = os.path.join(root, filename)
                 if ns.verbose:
                     print(path)
-                parse_file(path, force=ns.force, verify=not ns.force)
+                parse_file(path, verify=not ns.force)
         return
 
     if not ns.filename:
@@ -5103,7 +5119,7 @@ For more information see https://docs.python.org/3/howto/clinic.html""")
     for filename in ns.filename:
         if ns.verbose:
             print(filename)
-        parse_file(filename, output=ns.output, force=ns.force, verify=not ns.force)
+        parse_file(filename, output=ns.output, verify=not ns.force)
 
 
 if __name__ == "__main__":
index 8081b10..c5973f9 100644 (file)
@@ -37,11 +37,11 @@ function Sign-File {
 
     foreach ($a in $files) {
         if ($certsha1) {
-            SignTool sign /sha1 $certsha1 /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a
+            SignTool sign /sha1 $certsha1 /fd sha256 /tr http://timestamp.digicert.com/ /td sha256 /d $description $a
         } elseif ($certname) {
-            SignTool sign /a /n $certname /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a
+            SignTool sign /a /n $certname /fd sha256 /tr http://timestamp.digicert.com/ /td sha256 /d $description $a
         } elseif ($certfile) {
-            SignTool sign /f $certfile /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d $description $a
+            SignTool sign /f $certfile /fd sha256 /tr http://timestamp.digicert.com/ /td sha256 /d $description $a
         }
     }
 }
index 82a9115..9127ce8 100644 (file)
@@ -1,6 +1,6 @@
-<?xml version="1.0" encoding="UTF-8"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
-    <?define exts=_testcapi;_ctypes_test;_testbuffer;_testimportmultiple;_testmultiphase;_testconsole ?>
+    <?define exts=_testcapi;_ctypes_test;_testbuffer;_testimportmultiple;_testmultiphase;_testconsole;_testinternalcapi ?>
     <Fragment>
         <ComponentGroup Id="test_extensions">
             <?foreach ext in $(var.exts)?>
index 8f98e80..2da5f20 100644 (file)
@@ -13,6 +13,6 @@
   </metadata>
   <files>
     <file src="**\*" exclude="python.props" target="tools" />
-    <file src="python.props" target="build\native" />
+    <file src="python.props" target="build\native\python.props" />
   </files>
 </package>
index 273d79a..2d19793 100644 (file)
@@ -14,6 +14,7 @@
   </metadata>
   <files>
     <file src="**\*" exclude="python.props" target="tools" />
-    <file src="python.props" target="build\native" />
+    <file src="python.props" target="build\native\python.props" />
+    <file src="python.props" target="build\native\pythonarm32.props" />
   </files>
 </package>
index 5cf5580..7df1983 100644 (file)
@@ -13,6 +13,7 @@
   </metadata>
   <files>
     <file src="**\*" exclude="python.props" target="tools" />
-    <file src="python.props" target="build\native" />
+    <file src="python.props" target="build\native\python.props" />
+    <file src="python.props" target="build\native\pythondaily.props" />
   </files>
 </package>
index 27ef67e..ea878ba 100644 (file)
@@ -13,6 +13,7 @@
   </metadata>
   <files>
     <file src="**\*" exclude="python.props" target="tools" />
-    <file src="python.props" target="build\native" />
+    <file src="python.props" target="build\native\python.props" />
+    <file src="python.props" target="build\native\pythonx86.props" />
   </files>
 </package>
index aee668c..b4d6a0b 100644 (file)
@@ -496,6 +496,9 @@ class CParserGenerator(ParserGenerator, GrammarVisitor):
                 )
                 self.print("p->mark = _mark;")
                 self.print(f"void *_raw = {node.name}_raw(p);")
+                self.print("if (p->error_indicator)")
+                with self.indent():
+                    self.print("return NULL;")
                 self.print("if (_raw == NULL || p->mark <= _resmark)")
                 with self.indent():
                     self.print("break;")
@@ -730,7 +733,10 @@ class CParserGenerator(ParserGenerator, GrammarVisitor):
     def visit_Alt(
         self, node: Alt, is_loop: bool, is_gather: bool, rulename: Optional[str]
     ) -> None:
-        self.print(f"{{ // {node}")
+        if len(node.items) == 1 and str(node.items[0]).startswith('invalid_'):
+            self.print(f"if (p->call_invalid_rules) {{ // {node}")
+        else:
+            self.print(f"{{ // {node}")
         with self.indent():
             self._check_for_errors()
             node_str = str(node).replace('"', '\\"')
index 100db1d..6dfdbb9 100755 (executable)
@@ -5,14 +5,9 @@ import ast
 import os
 import sys
 import time
-import traceback
 import tokenize
-<<<<<<< HEAD
 import _peg_parser
-from glob import glob
-=======
 from glob import glob, escape
->>>>>>> 9355868458... bpo-41043: Escape literal part of the path for glob(). (GH-20994)
 from pathlib import PurePath
 
 from typing import List, Optional, Any, Tuple
index 9e6fd46..2d379fe 100755 (executable)
--- a/configure
+++ b/configure
@@ -1510,8 +1510,8 @@ Optional Packages:
                           specify the kind of universal binary that should be
                           created. this option is only valid when
                           --enable-universalsdk is set; options are:
-                          ("32-bit", "64-bit", "3-way", "intel", "intel-32",
-                          "intel-64", or "all") see Mac/README.rst
+                          ("universal2", "32-bit", "64-bit", "3-way", "intel",
+                          "intel-32", "intel-64", or "all") see Mac/README.rst
   --with-framework-name=FRAMEWORK
                           specify the name for the python framework on macOS
                           only valid when --enable-framework is set. see
@@ -6954,7 +6954,7 @@ fi
 
 
 
-# The -arch flags for universal builds on OSX
+# The -arch flags for universal builds on macOS
 UNIVERSAL_ARCH_FLAGS=
 
 
@@ -7481,6 +7481,11 @@ $as_echo "$CC" >&6; }
                LIPO_32BIT_FLAGS="-extract ppc7400 -extract i386"
                ARCH_RUN_32BIT="/usr/bin/arch -i386 -ppc"
                ;;
+            universal2)
+               UNIVERSAL_ARCH_FLAGS="-arch arm64 -arch x86_64"
+               LIPO_32BIT_FLAGS=""
+               ARCH_RUN_32BIT="true"
+               ;;
             intel)
                UNIVERSAL_ARCH_FLAGS="-arch i386 -arch x86_64"
                LIPO_32BIT_FLAGS="-extract i386"
@@ -7502,7 +7507,7 @@ $as_echo "$CC" >&6; }
                ARCH_RUN_32BIT="/usr/bin/arch -i386 -ppc"
                ;;
             *)
-               as_fn_error $? "proper usage is --with-universal-arch=32-bit|64-bit|all|intel|3-way" "$LINENO" 5
+               as_fn_error $? "proper usage is --with-universal-arch=universal2|32-bit|64-bit|all|intel|3-way" "$LINENO" 5
                ;;
             esac
 
@@ -7571,6 +7576,31 @@ $as_echo_n "checking which MACOSX_DEPLOYMENT_TARGET to use... " >&6; }
         { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MACOSX_DEPLOYMENT_TARGET" >&5
 $as_echo "$MACOSX_DEPLOYMENT_TARGET" >&6; }
 
+        { $as_echo "$as_me:${as_lineno-$LINENO}: checking if specified universal architectures work" >&5
+$as_echo_n "checking if specified universal architectures work... " >&6; }
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int
+main ()
+{
+printf("%d", 42);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+             as_fn_error $? "check config.log and use the '--with-universal-archs' option" "$LINENO" 5
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+
         # end of Darwin* tests
         ;;
     esac
@@ -9334,7 +9364,7 @@ fi
                MACOSX_DEFAULT_ARCH="ppc"
                ;;
        *)
-               as_fn_error $? "Unexpected output of 'arch' on OSX" "$LINENO" 5
+               as_fn_error $? "Unexpected output of 'arch' on macOS" "$LINENO" 5
                ;;
        esac
     else
@@ -9344,9 +9374,12 @@ fi
                ;;
        ppc)
                MACOSX_DEFAULT_ARCH="ppc64"
+               ;;
+       arm64)
+               MACOSX_DEFAULT_ARCH="arm64"
                ;;
        *)
-               as_fn_error $? "Unexpected output of 'arch' on OSX" "$LINENO" 5
+               as_fn_error $? "Unexpected output of 'arch' on macOS" "$LINENO" 5
                ;;
        esac
 
@@ -11989,6 +12022,31 @@ $as_echo "no" >&6; }
 
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _dyld_shared_cache_contains_path" >&5
+$as_echo_n "checking for _dyld_shared_cache_contains_path... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <mach-o/dyld.h>
+int
+main ()
+{
+void *x=_dyld_shared_cache_contains_path
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+$as_echo "#define HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH 1" >>confdefs.h
+
+   { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for memfd_create" >&5
 $as_echo_n "checking for memfd_create... " >&6; }
index d60f052..c968d14 100644 (file)
@@ -218,7 +218,7 @@ AC_ARG_WITH(universal-archs,
     AS_HELP_STRING([--with-universal-archs=ARCH],
                    [specify the kind of universal binary that should be created. this option is
                     only valid when --enable-universalsdk is set; options are:
-                    ("32-bit", "64-bit", "3-way", "intel", "intel-32", "intel-64", or "all")
+                    ("universal2", "32-bit", "64-bit", "3-way", "intel", "intel-32", "intel-64", or "all")
                     see Mac/README.rst]),
 [
        UNIVERSAL_ARCHS="$withval"
@@ -1587,7 +1587,7 @@ AC_SUBST(BASECFLAGS)
 AC_SUBST(CFLAGS_NODIST)
 AC_SUBST(LDFLAGS_NODIST)
 
-# The -arch flags for universal builds on OSX
+# The -arch flags for universal builds on macOS
 UNIVERSAL_ARCH_FLAGS=
 AC_SUBST(UNIVERSAL_ARCH_FLAGS)
 
@@ -1888,6 +1888,11 @@ yes)
                LIPO_32BIT_FLAGS="-extract ppc7400 -extract i386"
                ARCH_RUN_32BIT="/usr/bin/arch -i386 -ppc"
                ;;
+            universal2)
+               UNIVERSAL_ARCH_FLAGS="-arch arm64 -arch x86_64"
+               LIPO_32BIT_FLAGS=""
+               ARCH_RUN_32BIT="true"
+               ;;
             intel)
                UNIVERSAL_ARCH_FLAGS="-arch i386 -arch x86_64"
                LIPO_32BIT_FLAGS="-extract i386"
@@ -1909,7 +1914,7 @@ yes)
                ARCH_RUN_32BIT="/usr/bin/arch -i386 -ppc"
                ;;
             *)
-               AC_MSG_ERROR([proper usage is --with-universal-arch=32-bit|64-bit|all|intel|3-way])
+               AC_MSG_ERROR([proper usage is --with-universal-arch=universal2|32-bit|64-bit|all|intel|3-way])
                ;;
             esac
 
@@ -1976,6 +1981,13 @@ yes)
         EXPORT_MACOSX_DEPLOYMENT_TARGET=''
         AC_MSG_RESULT($MACOSX_DEPLOYMENT_TARGET)
 
+        AC_MSG_CHECKING(if specified universal architectures work)
+        AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <stdio.h>]], [[printf("%d", 42);]])],
+            [AC_MSG_RESULT(yes)],
+            [AC_MSG_RESULT(no)
+             AC_MSG_ERROR(check config.log and use the '--with-universal-archs' option)
+        ])
+
         # end of Darwin* tests
         ;;
     esac
@@ -2479,7 +2491,7 @@ case $ac_sys_system/$ac_sys_release in
                MACOSX_DEFAULT_ARCH="ppc"
                ;;
        *)
-               AC_MSG_ERROR([Unexpected output of 'arch' on OSX])
+               AC_MSG_ERROR([Unexpected output of 'arch' on macOS])
                ;;
        esac
     else
@@ -2489,9 +2501,12 @@ case $ac_sys_system/$ac_sys_release in
                ;;
        ppc)
                MACOSX_DEFAULT_ARCH="ppc64"
+               ;;
+       arm64)
+               MACOSX_DEFAULT_ARCH="arm64"
                ;;
        *)
-               AC_MSG_ERROR([Unexpected output of 'arch' on OSX])
+               AC_MSG_ERROR([Unexpected output of 'arch' on macOS])
                ;;
        esac
 
@@ -3770,6 +3785,12 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
    AC_MSG_RESULT(yes)],
   [AC_MSG_RESULT(no)
 ])
+AC_MSG_CHECKING(for _dyld_shared_cache_contains_path)
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <mach-o/dyld.h>]], [[void *x=_dyld_shared_cache_contains_path]])],
+  [AC_DEFINE(HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH, 1, Define if you have the '_dyld_shared_cache_contains_path' function.)
+   AC_MSG_RESULT(yes)],
+  [AC_MSG_RESULT(no)
+])
 
 AC_MSG_CHECKING(for memfd_create)
 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
index c9589cd..f39858d 100644 (file)
 /* Define if you have the 'prlimit' functions. */
 #undef HAVE_PRLIMIT
 
+/* Define if you have the '_dyld_shared_cache_contains_path' function. */
+#undef HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH
+
 /* Define to 1 if you have the <process.h> header file. */
 #undef HAVE_PROCESS_H
 
index 770866b..bd5f736 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -9,6 +9,7 @@ import re
 import sys
 import sysconfig
 from glob import glob, escape
+import _osx_support
 
 
 try:
@@ -178,32 +179,9 @@ def macosx_sdk_root():
         MACOS_SDK_ROOT = m.group(1)
         MACOS_SDK_SPECIFIED = MACOS_SDK_ROOT != '/'
     else:
-        MACOS_SDK_ROOT = '/'
+        MACOS_SDK_ROOT = _osx_support._default_sysroot(
+            sysconfig.get_config_var('CC'))
         MACOS_SDK_SPECIFIED = False
-        cc = sysconfig.get_config_var('CC')
-        tmpfile = '/tmp/setup_sdk_root.%d' % os.getpid()
-        try:
-            os.unlink(tmpfile)
-        except:
-            pass
-        ret = run_command('%s -E -v - </dev/null 2>%s 1>/dev/null' % (cc, tmpfile))
-        in_incdirs = False
-        try:
-            if ret == 0:
-                with open(tmpfile) as fp:
-                    for line in fp.readlines():
-                        if line.startswith("#include <...>"):
-                            in_incdirs = True
-                        elif line.startswith("End of search list"):
-                            in_incdirs = False
-                        elif in_incdirs:
-                            line = line.strip()
-                            if line == '/usr/include':
-                                MACOS_SDK_ROOT = '/'
-                            elif line.endswith(".sdk/usr/include"):
-                                MACOS_SDK_ROOT = line[:-12]
-        finally:
-            os.unlink(tmpfile)
 
     return MACOS_SDK_ROOT
 
@@ -239,6 +217,13 @@ def is_macosx_sdk_path(path):
                 or path.startswith('/Library/') )
 
 
+def grep_headers_for(function, headers):
+    for header in headers:
+        with open(header, 'r', errors='surrogateescape') as f:
+            if function in f.read():
+                return True
+    return False
+
 def find_file(filename, std_dirs, paths):
     """Searches for the directory where a given file is located,
     and returns a possibly-empty list of additional directories, or None
@@ -1028,7 +1013,7 @@ class PyBuildExt(build_ext):
             os_release = int(os.uname()[2].split('.')[0])
             dep_target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET')
             if (dep_target and
-                    (tuple(int(n) for n in dep_target.split('.')[0:2])
+                    (tuple(int(n) for n in str(dep_target).split('.')[0:2])
                         < (10, 5) ) ):
                 os_release = 8
             if os_release < 9:
@@ -1882,9 +1867,9 @@ class PyBuildExt(build_ext):
         # you want to build and link with a framework build of Tcl and Tk
         # that is not in /Library/Frameworks, say, in your private
         # $HOME/Library/Frameworks directory or elsewhere. It turns
-        # out to be difficult to make that work automtically here
+        # out to be difficult to make that work automatically here
         # without bringing into play more tools and magic. That case
-        # can be hamdled using a recipe with the right arguments
+        # can be handled using a recipe with the right arguments
         # to detect_tkinter_explicitly().
         #
         # Note also that the fallback case here is to try to use the
@@ -1892,7 +1877,7 @@ class PyBuildExt(build_ext):
         # be forewarned that they are deprecated by Apple and typically
         # out-of-date and buggy; their use should be avoided if at
         # all possible by installing a newer version of Tcl and Tk in
-        # /Library/Frameworks before bwfore building Python without
+        # /Library/Frameworks before building Python without
         # an explicit SDK or by configuring build arguments explicitly.
 
         from os.path import join, exists
@@ -1909,7 +1894,7 @@ class PyBuildExt(build_ext):
         else:
             # Use case #1: no explicit SDK selected.
             # Search the local system-wide /Library/Frameworks,
-            # not the one in the default SDK, othewise fall back to
+            # not the one in the default SDK, otherwise fall back to
             # /System/Library/Frameworks whose header files may be in
             # the default SDK or, on older systems, actually installed.
             framework_dirs = [
@@ -1925,7 +1910,7 @@ class PyBuildExt(build_ext):
                 if not exists(join(F, fw + '.framework')):
                     break
             else:
-                # ok, F is now directory with both frameworks. Continure
+                # ok, F is now directory with both frameworks. Continue
                 # building
                 break
         else:
@@ -2101,43 +2086,17 @@ class PyBuildExt(build_ext):
                            library_dirs=added_lib_dirs))
         return True
 
-    def configure_ctypes_darwin(self, ext):
-        # Darwin (OS X) uses preconfigured files, in
-        # the Modules/_ctypes/libffi_osx directory.
-        ffi_srcdir = os.path.abspath(os.path.join(self.srcdir, 'Modules',
-                                                  '_ctypes', 'libffi_osx'))
-        sources = [os.path.join(ffi_srcdir, p)
-                   for p in ['ffi.c',
-                             'x86/darwin64.S',
-                             'x86/x86-darwin.S',
-                             'x86/x86-ffi_darwin.c',
-                             'x86/x86-ffi64.c',
-                             'powerpc/ppc-darwin.S',
-                             'powerpc/ppc-darwin_closure.S',
-                             'powerpc/ppc-ffi_darwin.c',
-                             'powerpc/ppc64-darwin_closure.S',
-                             ]]
-
-        # Add .S (preprocessed assembly) to C compiler source extensions.
-        self.compiler.src_extensions.append('.S')
-
-        include_dirs = [os.path.join(ffi_srcdir, 'include'),
-                        os.path.join(ffi_srcdir, 'powerpc')]
-        ext.include_dirs.extend(include_dirs)
-        ext.sources.extend(sources)
-        return True
-
     def configure_ctypes(self, ext):
-        if not self.use_system_libffi:
-            if MACOS:
-                return self.configure_ctypes_darwin(ext)
-            print('INFO: Could not locate ffi libs and/or headers')
-            return False
         return True
 
     def detect_ctypes(self):
         # Thomas Heller's _ctypes module
-        self.use_system_libffi = False
+
+        if (not sysconfig.get_config_var("LIBFFI_INCLUDEDIR") and MACOS):
+            self.use_system_libffi = True
+        else:
+            self.use_system_libffi = '--with-system-ffi' in sysconfig.get_config_var("CONFIG_ARGS")
+
         include_dirs = []
         extra_compile_args = ['-DPy_BUILD_CORE_MODULE']
         extra_link_args = []
@@ -2150,11 +2109,9 @@ class PyBuildExt(build_ext):
 
         if MACOS:
             sources.append('_ctypes/malloc_closure.c')
-            sources.append('_ctypes/darwin/dlfcn_simple.c')
+            extra_compile_args.append('-DUSING_MALLOC_CLOSURE_DOT_C=1')
             extra_compile_args.append('-DMACOSX')
             include_dirs.append('_ctypes/darwin')
-            # XXX Is this still needed?
-            # extra_link_args.extend(['-read_only_relocs', 'warning'])
 
         elif HOST_PLATFORM == 'sunos5':
             # XXX This shouldn't be necessary; it appears that some
@@ -2184,31 +2141,48 @@ class PyBuildExt(build_ext):
                                sources=['_ctypes/_ctypes_test.c'],
                                libraries=['m']))
 
+        ffi_inc = sysconfig.get_config_var("LIBFFI_INCLUDEDIR")
+        ffi_lib = None
+
         ffi_inc_dirs = self.inc_dirs.copy()
         if MACOS:
-            if '--with-system-ffi' not in sysconfig.get_config_var("CONFIG_ARGS"):
-                return
-            # OS X 10.5 comes with libffi.dylib; the include files are
-            # in /usr/include/ffi
-            ffi_inc_dirs.append('/usr/include/ffi')
-
-        ffi_inc = [sysconfig.get_config_var("LIBFFI_INCLUDEDIR")]
-        if not ffi_inc or ffi_inc[0] == '':
-            ffi_inc = find_file('ffi.h', [], ffi_inc_dirs)
-        if ffi_inc is not None:
-            ffi_h = ffi_inc[0] + '/ffi.h'
+            ffi_in_sdk = os.path.join(macosx_sdk_root(), "usr/include/ffi")
+
+            if not ffi_inc:
+                if os.path.exists(ffi_in_sdk):
+                    ext.extra_compile_args.append("-DUSING_APPLE_OS_LIBFFI=1")
+                    ffi_inc = ffi_in_sdk
+                    ffi_lib = 'ffi'
+                else:
+                    # OS X 10.5 comes with libffi.dylib; the include files are
+                    # in /usr/include/ffi
+                    ffi_inc_dirs.append('/usr/include/ffi')
+
+        if not ffi_inc:
+            found = find_file('ffi.h', [], ffi_inc_dirs)
+            if found:
+                ffi_inc = found[0]
+        if ffi_inc:
+            ffi_h = ffi_inc + '/ffi.h'
             if not os.path.exists(ffi_h):
                 ffi_inc = None
                 print('Header file {} does not exist'.format(ffi_h))
-        ffi_lib = None
-        if ffi_inc is not None:
+        if ffi_lib is None and ffi_inc:
             for lib_name in ('ffi', 'ffi_pic'):
                 if (self.compiler.find_library_file(self.lib_dirs, lib_name)):
                     ffi_lib = lib_name
                     break
 
         if ffi_inc and ffi_lib:
-            ext.include_dirs.extend(ffi_inc)
+            ffi_headers = glob(os.path.join(ffi_inc, '*.h'))
+            if grep_headers_for('ffi_prep_cif_var', ffi_headers):
+                ext.extra_compile_args.append("-DHAVE_FFI_PREP_CIF_VAR=1")
+            if grep_headers_for('ffi_prep_closure_loc', ffi_headers):
+                ext.extra_compile_args.append("-DHAVE_FFI_PREP_CLOSURE_LOC=1")
+            if grep_headers_for('ffi_closure_alloc', ffi_headers):
+                ext.extra_compile_args.append("-DHAVE_FFI_CLOSURE_ALLOC=1")
+
+            ext.include_dirs.append(ffi_inc)
             ext.libraries.append(ffi_lib)
             self.use_system_libffi = True