Skip to content

Commit

Permalink
Merge branch 'main' into strptime-digits-old-glibc
Browse files Browse the repository at this point in the history
  • Loading branch information
serhiy-storchaka authored Oct 14, 2024
2 parents 0dabdbf + 5dac0dc commit 5bf57d6
Show file tree
Hide file tree
Showing 13 changed files with 227 additions and 52 deletions.
5 changes: 2 additions & 3 deletions Doc/reference/lexical_analysis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -284,11 +284,10 @@ UAX-31, with elaboration and changes as defined below; see also :pep:`3131` for
further details.

Within the ASCII range (U+0001..U+007F), the valid characters for identifiers
are the same as in Python 2.x: the uppercase and lowercase letters ``A`` through
include the uppercase and lowercase letters ``A`` through
``Z``, the underscore ``_`` and, except for the first character, the digits
``0`` through ``9``.

Python 3.0 introduces additional characters from outside the ASCII range (see
Python 3.0 introduced additional characters from outside the ASCII range (see
:pep:`3131`). For these characters, the classification uses the version of the
Unicode Character Database as included in the :mod:`unicodedata` module.

Expand Down
4 changes: 2 additions & 2 deletions Doc/using/configure.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Features and minimum versions required to build CPython:

* Tcl/Tk 8.5.12 for the :mod:`tkinter` module.

* Autoconf 2.71 and aclocal 1.16.4 are required to regenerate the
* Autoconf 2.71 and aclocal 1.16.5 are required to regenerate the
:file:`configure` script.

.. versionchanged:: 3.1
Expand All @@ -56,7 +56,7 @@ Features and minimum versions required to build CPython:
Tcl/Tk version 8.5.12 is now required for the :mod:`tkinter` module.

.. versionchanged:: 3.13
Autoconf 2.71, aclocal 1.16.4 and SQLite 3.15.2 are now required.
Autoconf 2.71, aclocal 1.16.5 and SQLite 3.15.2 are now required.

See also :pep:`7` "Style Guide for C Code" and :pep:`11` "CPython platform
support".
Expand Down
4 changes: 2 additions & 2 deletions Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2495,9 +2495,9 @@ Build Changes
* Building CPython now requires a compiler with support for the C11 atomic
library, GCC built-in atomic functions, or MSVC interlocked intrinsics.

* Autoconf 2.71 and aclocal 1.16.4 are now required to regenerate
* Autoconf 2.71 and aclocal 1.16.5 are now required to regenerate
the :file:`configure` script.
(Contributed by Christian Heimes in :gh:`89886`.)
(Contributed by Christian Heimes in :gh:`89886` and by Victor Stinner in :gh:`112090`.)

* SQLite 3.15.2 or newer is required to build
the :mod:`sqlite3` extension module.
Expand Down
10 changes: 8 additions & 2 deletions Lib/_pyrepl/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,13 @@ def _excepthook(self, typ, value, tb):

def runsource(self, source, filename="<input>", symbol="single"):
try:
tree = ast.parse(source)
tree = self.compile.compiler(
source,
filename,
"exec",
ast.PyCF_ONLY_AST,
incomplete_input=False,
)
except (SyntaxError, OverflowError, ValueError):
self.showsyntaxerror(filename, source=source)
return False
Expand All @@ -185,7 +191,7 @@ def runsource(self, source, filename="<input>", symbol="single"):
the_symbol = symbol if stmt is last_stmt else "exec"
item = wrapper([stmt])
try:
code = self.compile.compiler(item, filename, the_symbol, dont_inherit=True)
code = self.compile.compiler(item, filename, the_symbol)
except SyntaxError as e:
if e.args[0] == "'await' outside function":
python = os.path.basename(sys.executable)
Expand Down
6 changes: 2 additions & 4 deletions Lib/asyncio/futures.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,7 @@ def result(self):
the future is done and has an exception set, this exception is raised.
"""
if self._state == _CANCELLED:
exc = self._make_cancelled_error()
raise exc
raise self._make_cancelled_error()
if self._state != _FINISHED:
raise exceptions.InvalidStateError('Result is not ready.')
self.__log_traceback = False
Expand All @@ -208,8 +207,7 @@ def exception(self):
InvalidStateError.
"""
if self._state == _CANCELLED:
exc = self._make_cancelled_error()
raise exc
raise self._make_cancelled_error()
if self._state != _FINISHED:
raise exceptions.InvalidStateError('Exception is not set.')
self.__log_traceback = False
Expand Down
41 changes: 32 additions & 9 deletions Lib/asyncio/taskgroups.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,20 @@ async def __aenter__(self):
return self

async def __aexit__(self, et, exc, tb):
tb = None
try:
return await self._aexit(et, exc)
finally:
# Exceptions are heavy objects that can have object
# cycles (bad for GC); let's not keep a reference to
# a bunch of them. It would be nicer to use a try/finally
# in __aexit__ directly but that introduced some diff noise
self._parent_task = None
self._errors = None
self._base_error = None
exc = None

async def _aexit(self, et, exc):
self._exiting = True

if (exc is not None and
Expand Down Expand Up @@ -122,7 +136,10 @@ async def __aexit__(self, et, exc, tb):
assert not self._tasks

if self._base_error is not None:
raise self._base_error
try:
raise self._base_error
finally:
exc = None

if self._parent_cancel_requested:
# If this flag is set we *must* call uncancel().
Expand All @@ -133,8 +150,14 @@ async def __aexit__(self, et, exc, tb):

# Propagate CancelledError if there is one, except if there
# are other errors -- those have priority.
if propagate_cancellation_error is not None and not self._errors:
raise propagate_cancellation_error
try:
if propagate_cancellation_error is not None and not self._errors:
try:
raise propagate_cancellation_error
finally:
exc = None
finally:
propagate_cancellation_error = None

if et is not None and not issubclass(et, exceptions.CancelledError):
self._errors.append(exc)
Expand All @@ -146,14 +169,14 @@ async def __aexit__(self, et, exc, tb):
if self._parent_task.cancelling():
self._parent_task.uncancel()
self._parent_task.cancel()
# Exceptions are heavy objects that can have object
# cycles (bad for GC); let's not keep a reference to
# a bunch of them.
try:
me = BaseExceptionGroup('unhandled errors in a TaskGroup', self._errors)
raise me from None
raise BaseExceptionGroup(
'unhandled errors in a TaskGroup',
self._errors,
) from None
finally:
self._errors = None
exc = None


def create_task(self, coro, *, name=None, context=None):
"""Create a new task in this group and return it.
Expand Down
7 changes: 5 additions & 2 deletions Lib/codeop.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
# Caveat emptor: These flags are undocumented on purpose and depending
# on their effect outside the standard library is **unsupported**.
PyCF_DONT_IMPLY_DEDENT = 0x200
PyCF_ONLY_AST = 0x400
PyCF_ALLOW_INCOMPLETE_INPUT = 0x4000

def _maybe_compile(compiler, source, filename, symbol):
Expand Down Expand Up @@ -109,12 +110,14 @@ class Compile:
def __init__(self):
self.flags = PyCF_DONT_IMPLY_DEDENT | PyCF_ALLOW_INCOMPLETE_INPUT

def __call__(self, source, filename, symbol, **kwargs):
flags = self.flags
def __call__(self, source, filename, symbol, flags=0, **kwargs):
flags |= self.flags
if kwargs.get('incomplete_input', True) is False:
flags &= ~PyCF_DONT_IMPLY_DEDENT
flags &= ~PyCF_ALLOW_INCOMPLETE_INPUT
codeob = compile(source, filename, symbol, flags, True)
if flags & PyCF_ONLY_AST:
return codeob # this is an ast.Module in this case
for feature in _features:
if codeob.co_flags & feature.compiler_flag:
self.flags |= feature.compiler_flag
Expand Down
22 changes: 22 additions & 0 deletions Lib/test/test_asyncio/test_futures.py
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,28 @@ def __del__(self):
fut = self._new_future(loop=self.loop)
fut.set_result(Evil())

def test_future_cancelled_result_refcycles(self):
f = self._new_future(loop=self.loop)
f.cancel()
exc = None
try:
f.result()
except asyncio.CancelledError as e:
exc = e
self.assertIsNotNone(exc)
self.assertListEqual(gc.get_referrers(exc), [])

def test_future_cancelled_exception_refcycles(self):
f = self._new_future(loop=self.loop)
f.cancel()
exc = None
try:
f.exception()
except asyncio.CancelledError as e:
exc = e
self.assertIsNotNone(exc)
self.assertListEqual(gc.get_referrers(exc), [])


@unittest.skipUnless(hasattr(futures, '_CFuture'),
'requires the C _asyncio module')
Expand Down
92 changes: 90 additions & 2 deletions Lib/test/test_asyncio/test_taskgroups.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Adapted with permission from the EdgeDB project;
# license: PSFL.


import gc
import asyncio
import contextvars
import contextlib
Expand All @@ -11,7 +11,6 @@

from test.test_asyncio.utils import await_without_task


# To prevent a warning "test altered the execution environment"
def tearDownModule():
asyncio.set_event_loop_policy(None)
Expand Down Expand Up @@ -899,6 +898,95 @@ async def outer():

await outer()

async def test_exception_refcycles_direct(self):
"""Test that TaskGroup doesn't keep a reference to the raised ExceptionGroup"""
tg = asyncio.TaskGroup()
exc = None

class _Done(Exception):
pass

try:
async with tg:
raise _Done
except ExceptionGroup as e:
exc = e

self.assertIsNotNone(exc)
self.assertListEqual(gc.get_referrers(exc), [])


async def test_exception_refcycles_errors(self):
"""Test that TaskGroup deletes self._errors, and __aexit__ args"""
tg = asyncio.TaskGroup()
exc = None

class _Done(Exception):
pass

try:
async with tg:
raise _Done
except* _Done as excs:
exc = excs.exceptions[0]

self.assertIsInstance(exc, _Done)
self.assertListEqual(gc.get_referrers(exc), [])


async def test_exception_refcycles_parent_task(self):
"""Test that TaskGroup deletes self._parent_task"""
tg = asyncio.TaskGroup()
exc = None

class _Done(Exception):
pass

async def coro_fn():
async with tg:
raise _Done

try:
async with asyncio.TaskGroup() as tg2:
tg2.create_task(coro_fn())
except* _Done as excs:
exc = excs.exceptions[0].exceptions[0]

self.assertIsInstance(exc, _Done)
self.assertListEqual(gc.get_referrers(exc), [])

async def test_exception_refcycles_propagate_cancellation_error(self):
"""Test that TaskGroup deletes propagate_cancellation_error"""
tg = asyncio.TaskGroup()
exc = None

try:
async with asyncio.timeout(-1):
async with tg:
await asyncio.sleep(0)
except TimeoutError as e:
exc = e.__cause__

self.assertIsInstance(exc, asyncio.CancelledError)
self.assertListEqual(gc.get_referrers(exc), [])

async def test_exception_refcycles_base_error(self):
"""Test that TaskGroup deletes self._base_error"""
class MyKeyboardInterrupt(KeyboardInterrupt):
pass

tg = asyncio.TaskGroup()
exc = None

try:
async with tg:
raise MyKeyboardInterrupt
except MyKeyboardInterrupt as e:
exc = e

self.assertIsNotNone(exc)
self.assertListEqual(gc.get_referrers(exc), [])


if __name__ == "__main__":
unittest.main()
27 changes: 26 additions & 1 deletion Lib/test/test_pyrepl/test_interact.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,38 @@ def test_runsource_shows_syntax_error_for_failed_compilation(self):

def test_no_active_future(self):
console = InteractiveColoredConsole()
source = "x: int = 1; print(__annotate__(1))"
source = dedent("""\
x: int = 1
print(__annotate__(1))
""")
f = io.StringIO()
with contextlib.redirect_stdout(f):
result = console.runsource(source)
self.assertFalse(result)
self.assertEqual(f.getvalue(), "{'x': <class 'int'>}\n")

def test_future_annotations(self):
console = InteractiveColoredConsole()
source = dedent("""\
from __future__ import annotations
def g(x: int): ...
print(g.__annotations__)
""")
f = io.StringIO()
with contextlib.redirect_stdout(f):
result = console.runsource(source)
self.assertFalse(result)
self.assertEqual(f.getvalue(), "{'x': 'int'}\n")

def test_future_barry_as_flufl(self):
console = InteractiveColoredConsole()
f = io.StringIO()
with contextlib.redirect_stdout(f):
result = console.runsource("from __future__ import barry_as_FLUFL\n")
result = console.runsource("""print("black" <> 'blue')\n""")
self.assertFalse(result)
self.assertEqual(f.getvalue(), "True\n")


class TestMoreLines(unittest.TestCase):
def test_invalid_syntax_single_line(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix refcycles in exceptions raised from :class:`asyncio.TaskGroup` and the python implementation of :class:`asyncio.Future`
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix support for the ``barry_as_FLUFL`` future flag in the new REPL.
Loading

0 comments on commit 5bf57d6

Please sign in to comment.