From e8477910aaf88ce4df5b053e7cf43bf2cf814bc2 Mon Sep 17 00:00:00 2001 From: Matthias Klose Date: Mon, 19 Oct 2020 10:51:18 +0100 Subject: [PATCH] git-updates # DP: updates from the 3.9 branch (until 2020-10-19). # DP: updates from the 3.9 branch (until 2020-10-19). # git diff --no-renames 9cf6752276e6fcfd0c23fdb064ad27f448aaaf75 7c949020ef2520d7a7cbc978f0b34423e6c2a94c | filterdiff -x ?/.hgignore -x ?/.hgeol -x ?/.hgtags -x ?/.hgtouch -x ?/.gitignore -x ?/.gitattributes -x '?/.github/*' -x '?/.git*' -x ?/.codecov.yml -x ?/.travis.yml -x ?/configure --remove-timestamps Gbp-Pq: Name git-updates.diff --- Doc/faq/programming.rst | 37 +++++------ Doc/library/idle.rst | 47 +++++++------- Doc/library/math.rst | 2 +- Doc/library/random.rst | 61 ++++++++++++++++++- Doc/library/shutil.rst | 2 +- Doc/library/site.rst | 4 +- Doc/library/typing.rst | 6 +- Doc/library/unittest.rst | 3 +- Doc/library/xml.etree.elementtree.rst | 6 ++ Doc/reference/datamodel.rst | 15 ++--- Doc/tools/extensions/pyspecific.py | 36 ++++++----- Doc/whatsnew/3.8.rst | 10 +++ Doc/whatsnew/3.9.rst | 56 ++++++++--------- Grammar/python.gram | 2 +- Include/cpython/fileobject.h | 8 --- Include/fileobject.h | 7 +++ Include/patchlevel.h | 2 +- Lib/cProfile.py | 5 ++ Lib/ctypes/test/test_find.py | 12 +++- Lib/ctypes/util.py | 30 +++++++-- Lib/datetime.py | 2 +- Lib/graphlib.py | 7 ++- Lib/idlelib/NEWS.txt | 14 ++++- Lib/idlelib/calltip.py | 4 -- Lib/idlelib/help.html | 43 +++++++------ Lib/idlelib/idle_test/test_calltip.py | 8 +-- Lib/idlelib/pyshell.py | 2 +- Lib/logging/__init__.py | 6 +- Lib/profile.py | 5 ++ Lib/pstats.py | 6 +- Lib/symtable.py | 23 ++++--- Lib/test/datetimetester.py | 3 + Lib/test/multibytecodec_support.py | 22 +++---- Lib/test/test_codecs.py | 12 ++++ Lib/test/test_finalization.py | 23 ++++++- Lib/test/test_gc.py | 6 +- Lib/test/test_genericalias.py | 4 +- Lib/test/test_isinstance.py | 10 +++ Lib/test/test_lib2to3.py | 4 +- Lib/test/test_logging.py | 21 ++++--- Lib/test/test_profile.py | 16 ++++- Lib/test/test_pstats.py | 4 ++ Lib/test/test_site.py | 4 +- Lib/test/test_symtable.py | 18 +++++- Lib/test/test_with.py | 8 ++- Lib/test/test_xml_etree.py | 8 +++ Lib/test/test_zipfile.py | 3 + Lib/test/test_zoneinfo/test_zoneinfo.py | 3 +- Lib/tkinter/__init__.py | 17 +++--- Lib/tkinter/test/test_tkinter/test_misc.py | 48 +++++++++++++++ Lib/tkinter/test/test_tkinter/test_widgets.py | 3 +- Lib/xml/etree/ElementTree.py | 5 ++ Lib/zipfile.py | 2 + Mac/BuildScript/build-installer.py | 6 +- Misc/ACKS | 1 + Modules/_testcapimodule.c | 20 ++++++ Modules/_tracemalloc.c | 2 +- Objects/abstract.c | 2 - Objects/genericaliasobject.c | 2 +- Objects/methodobject.c | 6 +- Objects/typeobject.c | 22 ++----- Objects/unicodeobject.c | 4 +- Parser/pegen/parse.c | 15 +++-- Python/dynload_aix.c | 14 +++-- Python/dynload_hpux.c | 15 ++++- Python/dynload_shlib.c | 4 +- Python/import.c | 23 ++++--- Tools/c-analyzer/c_analyzer/common/files.py | 4 +- setup.py | 10 +-- 69 files changed, 592 insertions(+), 273 deletions(-) diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index 66d210a..106450f 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -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? ------------------------------------------------------------------------------------- @@ -1164,6 +1158,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 +1375,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 ======= diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst index 43096b0..a59a5d3 100644 --- a/Doc/library/idle.rst +++ b/Doc/library/idle.rst @@ -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: diff --git a/Doc/library/math.rst b/Doc/library/math.rst index 6ec1fee..b20e557 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -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``. diff --git a/Doc/library/random.rst b/Doc/library/random.rst index 90366f4..f9535d7 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -253,6 +253,8 @@ Functions for sequences order so that the sample is reproducible. +.. _real-valued-distributions: + Real-valued distributions ------------------------- @@ -391,8 +393,8 @@ change across Python versions, but two aspects are guaranteed not to change: .. _random-examples: -Examples and Recipes --------------------- +Examples +-------- Basic examples:: @@ -536,3 +538,58 @@ Simulation of arrival times and service deliveries for a multiserver queue:: a tutorial by `Peter Norvig `_ 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 ` +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 + `_ a + paper by Allen B. Downey describing ways to generate more + fine-grained floats than normally generated by :func:`.random`. diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst index 1b094ae..fd3ce74 100644 --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -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 diff --git a/Doc/library/site.rst b/Doc/library/site.rst index b424e1b..2e3646f 100644 --- a/Doc/library/site.rst +++ b/Doc/library/site.rst @@ -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 diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 3900e49..e8f34e4 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1461,7 +1461,7 @@ 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`. .. class:: AsyncContextManager(Generic[T_co]) @@ -1471,7 +1471,7 @@ 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`. Protocols --------- @@ -1691,7 +1691,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`). diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index 285bb9e..8a4fd25 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -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 diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst index 7725e4d..f4bccf6 100644 --- a/Doc/library/xml.etree.elementtree.rst +++ b/Doc/library/xml.etree.elementtree.rst @@ -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 diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index fc304a1..354c6ce 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2376,10 +2376,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 +2779,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. diff --git a/Doc/tools/extensions/pyspecific.py b/Doc/tools/extensions/pyspecific.py index f08f4ab..2fad9ec 100644 --- a/Doc/tools/extensions/pyspecific.py +++ b/Doc/tools/extensions/pyspecific.py @@ -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 diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index a2fa178..6a9fa34 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -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 ------- diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 9a09e71..da07185 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -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. diff --git a/Grammar/python.gram b/Grammar/python.gram index 334a7f5..8f4d250 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -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) } diff --git a/Include/cpython/fileobject.h b/Include/cpython/fileobject.h index 57eac13..3005ce1 100644 --- a/Include/cpython/fileobject.h +++ b/Include/cpython/fileobject.h @@ -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); diff --git a/Include/fileobject.h b/Include/fileobject.h index 456887e..6ec2994 100644 --- a/Include/fileobject.h +++ b/Include/fileobject.h @@ -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 */ diff --git a/Include/patchlevel.h b/Include/patchlevel.h index a9e8ef1..2243329 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -23,7 +23,7 @@ #define PY_RELEASE_SERIAL 0 /* Version as a string */ -#define PY_VERSION "3.9.0" +#define PY_VERSION "3.9.0+" /*--end constants--*/ /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. diff --git a/Lib/cProfile.py b/Lib/cProfile.py index 4f20203..59b4699 100755 --- a/Lib/cProfile.py +++ b/Lib/cProfile.py @@ -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__')" diff --git a/Lib/ctypes/test/test_find.py b/Lib/ctypes/test/test_find.py index b99fdcb..92ac184 100644 --- a/Lib/ctypes/test/test_find.py +++ b/Lib/ctypes/test/test_find.py @@ -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() diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py index 01176bf..0c2510e 100644 --- a/Lib/ctypes/util.py +++ b/Lib/ctypes/util.py @@ -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 diff --git a/Lib/datetime.py b/Lib/datetime.py index 2294ac2..e508d99 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -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) diff --git a/Lib/graphlib.py b/Lib/graphlib.py index 948f62f..d0e7a48 100644 --- a/Lib/graphlib.py +++ b/Lib/graphlib.py @@ -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 diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index fd76207..5ea3322 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -1,8 +1,20 @@ +What's New in IDLE 3.9.1 +Released on 2020-12-07? +====================================== + + +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). diff --git a/Lib/idlelib/calltip.py b/Lib/idlelib/calltip.py index d4092c7..b02f872 100644 --- a/Lib/idlelib/calltip.py +++ b/Lib/idlelib/calltip.py @@ -118,7 +118,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 ''. @@ -146,9 +145,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 diff --git a/Lib/idlelib/help.html b/Lib/idlelib/help.html index b2853cf..0edd391 100644 --- a/Lib/idlelib/help.html +++ b/Lib/idlelib/help.html @@ -509,26 +509,29 @@ by typing ‘_’ after ‘.’, either before or after the box is opened.

Calltips¶

-

A calltip is shown when one types ( after the name of an accessible -function. A name expression may include dots and subscripts. A calltip -remains until it is clicked, the cursor is moved out of the argument area, -or ) 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 ( 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 ) 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¶

@@ -975,7 +978,7 @@ also used for testing.



- Last updated on Sep 09, 2020. + Last updated on Sep 22, 2020. Found a bug?
diff --git a/Lib/idlelib/idle_test/test_calltip.py b/Lib/idlelib/idle_test/test_calltip.py index d386b5c..4d53df1 100644 --- a/Lib/idlelib/idle_test/test_calltip.py +++ b/Lib/idlelib/idle_test/test_calltip.py @@ -61,18 +61,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) diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 66ae0f7..b69916d 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -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 diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 403dc81..7b169a1 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -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 diff --git a/Lib/profile.py b/Lib/profile.py index aad458d..5cb017e 100755 --- a/Lib/profile.py +++ b/Lib/profile.py @@ -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 diff --git a/Lib/pstats.py b/Lib/pstats.py index e781b91..0f93ae0 100644 --- a/Lib/pstats.py +++ b/Lib/pstats.py @@ -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 diff --git a/Lib/symtable.py b/Lib/symtable.py index a711676..521540f 100644 --- a/Lib/symtable.py +++ b/Lib/symtable.py @@ -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 "".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) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index a9741d6..b37ef91 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -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 = [ diff --git a/Lib/test/multibytecodec_support.py b/Lib/test/multibytecodec_support.py index cca8af6..f76c015 100644 --- a/Lib/test/multibytecodec_support.py +++ b/Lib/test/multibytecodec_support.py @@ -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]) diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index 54a3520..8d112a1 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -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') diff --git a/Lib/test/test_finalization.py b/Lib/test/test_finalization.py index 35d7913..1d13443 100644 --- a/Lib/test/test_finalization.py +++ b/Lib/test/test_finalization.py @@ -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() diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py index acb6391..38c9cb7 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -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 diff --git a/Lib/test/test_genericalias.py b/Lib/test/test_genericalias.py index ec1acd4..240aad0 100644 --- a/Lib/test/test_genericalias.py +++ b/Lib/test/test_genericalias.py @@ -26,7 +26,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 @@ -79,7 +79,7 @@ class BaseTest(unittest.TestCase): SplitResult, ParseResult, ValueProxy, ApplyResult, WeakSet, ReferenceType, ref, - ShareableList, SimpleQueue, + ShareableList, MPSimpleQueue, Future, _WorkItem, Morsel, ): diff --git a/Lib/test/test_isinstance.py b/Lib/test/test_isinstance.py index 53639e9..31b3899 100644 --- a/Lib/test/test_isinstance.py +++ b/Lib/test/test_isinstance.py @@ -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 diff --git a/Lib/test/test_lib2to3.py b/Lib/test/test_lib2to3.py index 15c317e..e939d47 100644 --- a/Lib/test/test_lib2to3.py +++ b/Lib/test/test_lib2to3.py @@ -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() diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 62759c0..410eae2 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -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): diff --git a/Lib/test/test_profile.py b/Lib/test/test_profile.py index 01a8a6e..2336498 100644 --- a/Lib/test/test_profile.py +++ b/Lib/test/test_profile.py @@ -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') diff --git a/Lib/test/test_pstats.py b/Lib/test/test_pstats.py index 10559de..4f78b99 100644 --- a/Lib/test/test_pstats.py +++ b/Lib/test/test_pstats.py @@ -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() diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index 923f35e..ffba139 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -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: diff --git a/Lib/test/test_symtable.py b/Lib/test/test_symtable.py index d193558..6585071 100644 --- a/Lib/test/test_symtable.py +++ b/Lib/test/test_symtable.py @@ -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), "") + self.assertEqual(str(self.spam), "") + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_with.py b/Lib/test/test_with.py index b1d7a15..f21bf65 100644 --- a/Lib/test/test_with.py +++ b/Lib/test/test_with.py @@ -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): diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index d01649d..5632b8b 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -3894,6 +3894,14 @@ class C14NTest(unittest.TestCase): #self.assertEqual(c14n_roundtrip(""), #'') + # Namespace issues + xml = '' + self.assertEqual(c14n_roundtrip(xml), xml) + xml = '' + self.assertEqual(c14n_roundtrip(xml), xml) + xml = '' + self.assertEqual(c14n_roundtrip(xml), xml) + def test_c14n_exclusion(self): xml = textwrap.dedent("""\ diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index b7bc218..999197a 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -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") diff --git a/Lib/test/test_zoneinfo/test_zoneinfo.py b/Lib/test/test_zoneinfo/test_zoneinfo.py index 8570326..d4704b7 100644 --- a/Lib/test/test_zoneinfo/test_zoneinfo.py +++ b/Lib/test/test_zoneinfo/test_zoneinfo.py @@ -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: diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 1067ab6..2175afc 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -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) ) diff --git a/Lib/tkinter/test/test_tkinter/test_misc.py b/Lib/tkinter/test/test_tkinter/test_misc.py index 1e08974..b8eea25 100644 --- a/Lib/tkinter/test/test_tkinter/test_misc.py +++ b/Lib/tkinter/test/test_tkinter/test_misc.py @@ -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), + "") tests_gui = (MiscTest, ) diff --git a/Lib/tkinter/test/test_tkinter/test_widgets.py b/Lib/tkinter/test/test_tkinter/test_widgets.py index 721e813..b6f792d 100644 --- a/Lib/tkinter/test/test_tkinter/test_widgets.py +++ b/Lib/tkinter/test/test_tkinter/test_widgets.py @@ -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() diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py index da2bcad..7a26900 100644 --- a/Lib/xml/etree/ElementTree.py +++ b/Lib/xml/etree/ElementTree.py @@ -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): diff --git a/Lib/zipfile.py b/Lib/zipfile.py index 915698f..816f858 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -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): diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index a58b922..2548b21 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -307,9 +307,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 ' diff --git a/Misc/ACKS b/Misc/ACKS index a16f15a..3125600 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -781,6 +781,7 @@ Meador Inge Peter Ingebretson Tony Ingraldi John Interrante +Dean Inwood Bob Ippolito Roger Irwin Atsuo Ishimoto diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index b2d070c..54c1e62 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -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 */ }; diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c index fc91622..04f6c24 100644 --- a/Modules/_tracemalloc.c +++ b/Modules/_tracemalloc.c @@ -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; diff --git a/Objects/abstract.c b/Objects/abstract.c index 1d671c9..1922619 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -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; diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c index 4f95216..3e850b5 100644 --- a/Objects/genericaliasobject.c +++ b/Objects/genericaliasobject.c @@ -582,7 +582,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, diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 5659f21..7b43041 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -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; } diff --git a/Objects/typeobject.c b/Objects/typeobject.c index f65434f..1a12b0c 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -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__); diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 4c8c880..d5fbf15 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -8160,7 +8160,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 +15602,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/Parser/pegen/parse.c b/Parser/pegen/parse.c index f74b6f2..1a4df75 100644 --- a/Parser/pegen/parse.c +++ b/Parser/pegen/parse.c @@ -4289,7 +4289,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 +4303,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 +4317,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,7 +4333,7 @@ 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->error_indicator) { diff --git a/Python/dynload_aix.c b/Python/dynload_aix.c index 684f10a..97f7698 100644 --- a/Python/dynload_aix.c +++ b/Python/dynload_aix.c @@ -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; diff --git a/Python/dynload_hpux.c b/Python/dynload_hpux.c index 4b964a6..e36d608 100644 --- a/Python/dynload_hpux.c +++ b/Python/dynload_hpux.c @@ -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); diff --git a/Python/dynload_shlib.c b/Python/dynload_shlib.c index 082154d..2382889 100644 --- a/Python/dynload_shlib.c +++ b/Python/dynload_shlib.c @@ -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); diff --git a/Python/import.c b/Python/import.c index 0e2e7c3..5e39a2f 100644 --- a/Python/import.c +++ b/Python/import.c @@ -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); } diff --git a/Tools/c-analyzer/c_analyzer/common/files.py b/Tools/c-analyzer/c_analyzer/common/files.py index f630afe..a8a0447 100644 --- a/Tools/c-analyzer/c_analyzer/common/files.py +++ b/Tools/c-analyzer/c_analyzer/common/files.py @@ -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: diff --git a/setup.py b/setup.py index 770866b..0a6cd8c 100644 --- a/setup.py +++ b/setup.py @@ -1882,9 +1882,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 +1892,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 +1909,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 +1925,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: -- 2.30.2