Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Doc/c-api/complex.rst
Original file line number Diff line number Diff line change
Expand Up @@ -197,3 +197,6 @@ the :ref:`Number Protocol <number>` API or use native complex types, like
Set :c:data:`errno` to :c:macro:`!ERANGE` on overflows.

.. deprecated:: 3.15

.. versionchanged:: next
This function leaves :c:data:`errno` unchanged on success.
6 changes: 3 additions & 3 deletions Doc/library/socket.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1497,7 +1497,7 @@ Socket Objects
of :meth:`socket.getpeername` but not the actual OS resource. Unlike
:func:`socket.fromfd`, *fileno* will return the same socket and not a
duplicate. This may help close a detached socket using
:meth:`socket.close`.
:meth:`~socket.socket.close`.

The newly created socket is :ref:`non-inheritable <fd_inheritance>`.

Expand Down Expand Up @@ -1544,7 +1544,7 @@ Socket Objects

.. versionchanged:: 3.2
Support for the :term:`context manager` protocol was added. Exiting the
context manager is equivalent to calling :meth:`~socket.close`.
context manager is equivalent to calling :meth:`~socket.socket.close`.


.. method:: accept()
Expand Down Expand Up @@ -1769,7 +1769,7 @@ Socket Objects

Closing the file object returned by :meth:`makefile` won't close the
original socket unless all other file objects have been closed and
:meth:`socket.close` has been called on the socket object.
:meth:`~socket.socket.close` has been called on the socket object.

.. note::

Expand Down
43 changes: 23 additions & 20 deletions Doc/library/warnings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -68,45 +68,48 @@ The following warnings category classes are currently defined:
+----------------------------------+-----------------------------------------------+
| Class | Description |
+==================================+===============================================+
| :exc:`Warning` | This is the base class of all warning |
| | category classes. It is a subclass of |
| | :exc:`Exception`. |
| :exc:`Warning` | Base class for warning categories. It is a |
| | subclass of :exc:`Exception`. |
+----------------------------------+-----------------------------------------------+
| :exc:`UserWarning` | The default category for :func:`warn`. |
| :exc:`UserWarning` | Base class for warnings generated by user |
| | code. The default category for :func:`warn`. |
+----------------------------------+-----------------------------------------------+
| :exc:`DeprecationWarning` | Base category for warnings about deprecated |
| :exc:`DeprecationWarning` | Base class for warnings about deprecated |
| | features when those warnings are intended for |
| | other Python developers (ignored by default, |
| | unless triggered by code in ``__main__``). |
+----------------------------------+-----------------------------------------------+
| :exc:`SyntaxWarning` | Base category for warnings about dubious |
| | syntactic features (typically emitted when |
| | compiling Python source code, and hence |
| | may not be suppressed by runtime filters) |
| :exc:`PendingDeprecationWarning` | Base class for warnings about features |
| | that will be deprecated in the future |
| | (ignored by default). |
+----------------------------------+-----------------------------------------------+
| :exc:`RuntimeWarning` | Base category for warnings about dubious |
| | runtime features. |
| :exc:`SyntaxWarning` | Base class for warnings about dubious syntax |
| | (typically emitted when compiling Python |
| | source code, and hence may not be suppressed |
| | by runtime filters). |
+----------------------------------+-----------------------------------------------+
| :exc:`FutureWarning` | Base category for warnings about deprecated |
| :exc:`RuntimeWarning` | Base class for warnings about dubious runtime |
| | behavior. |
+----------------------------------+-----------------------------------------------+
| :exc:`FutureWarning` | Base class for warnings about deprecated |
| | features when those warnings are intended for |
| | end users of applications that are written in |
| | Python. |
+----------------------------------+-----------------------------------------------+
| :exc:`PendingDeprecationWarning` | Base category for warnings about features |
| | that will be deprecated in the future |
| | (ignored by default). |
+----------------------------------+-----------------------------------------------+
| :exc:`ImportWarning` | Base category for warnings triggered during |
| :exc:`ImportWarning` | Base class for warnings triggered during |
| | the process of importing a module (ignored by |
| | default). |
+----------------------------------+-----------------------------------------------+
| :exc:`UnicodeWarning` | Base category for warnings related to |
| :exc:`UnicodeWarning` | Base class for warnings related to |
| | Unicode. |
+----------------------------------+-----------------------------------------------+
| :exc:`BytesWarning` | Base category for warnings related to |
| :exc:`EncodingWarning` | Base class for warnings related to encodings. |
| | See :ref:`io-encoding-warning` for details. |
+----------------------------------+-----------------------------------------------+
| :exc:`BytesWarning` | Base class for warnings related to |
| | :class:`bytes` and :class:`bytearray`. |
+----------------------------------+-----------------------------------------------+
| :exc:`ResourceWarning` | Base category for warnings related to |
| :exc:`ResourceWarning` | Base class for warnings related to |
| | resource usage (ignored by default). |
+----------------------------------+-----------------------------------------------+

Expand Down
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,10 @@ Porting to Python 3.16
if the value cannot be marshalled.
(Contributed by Serhiy Storchaka in :gh:`155907`.)

* :c:func:`_Py_c_abs` no longer sets :c:data:`errno` to zero on success,
but rather leaves it unchanged.
(Contributed by Sergey B Kirpichev in :gh:`155526`.)

Deprecated C APIs
-----------------

Expand Down
18 changes: 17 additions & 1 deletion Lib/asyncio/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,9 @@ def __step(self, exc=None):
raise exceptions.InvalidStateError(
f'__step(): already done: {self!r}, {exc!r}')
if self._must_cancel:
if not isinstance(exc, exceptions.CancelledError):
# gh-108549: do not swallow SystemExit and KeyboardInterrupt.
if not isinstance(exc, (exceptions.CancelledError,
SystemExit, KeyboardInterrupt)):
exc = self._make_cancelled_error()
self._must_cancel = False
self._fut_waiter = None
Expand Down Expand Up @@ -541,13 +543,18 @@ async def _cancel_and_wait(fut):
cb = functools.partial(_release_waiter, waiter)
fut.add_done_callback(cb)

# gh-157058: awaiting the waiter leaves no edge on fut, add it here
cur_task = current_task()
futures.future_add_to_awaited_by(fut, cur_task)

try:
fut.cancel()
# We cannot wait on *fut* directly to make
# sure _cancel_and_wait itself is reliably cancellable.
await waiter
finally:
fut.remove_done_callback(cb)
futures.future_discard_from_awaited_by(fut, cur_task)


class _AsCompletedIterator:
Expand Down Expand Up @@ -770,6 +777,11 @@ def cancel(self, msg=None):
return ret


def _discard_awaited_by(children, waiter, outer):
for fut in children:
futures.future_discard_from_awaited_by(fut, waiter)


def gather(*coros_or_futures, return_exceptions=False):
"""Return a future aggregating results from the given coroutines/futures.

Expand Down Expand Up @@ -903,6 +915,10 @@ def _done_callback(fut, cur_task=cur_task):
children.append(fut)

outer = _GatheringFuture(children, loop=loop)
if cur_task is not None:
# gh-157213: a child outliving gather() must lose the awaited-by edge
outer.add_done_callback(
functools.partial(_discard_awaited_by, children, cur_task))
# Run done callbacks after GatheringFuture created so any post-processing
# can be performed at this point
# optimization: in the special case that *all* futures finished eagerly,
Expand Down
3 changes: 2 additions & 1 deletion Lib/base64.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,8 @@ def b16decode(s, casefold=False, *, ignorechars=b''):
for b in b'abcdef':
if b in s and b not in ignorechars:
raise binascii.Error('Non-base16 digit found')
s = s.translate(None, delete=b'abcdef')
if ignorechars:
s = s.translate(None, delete=b'abcdef')
return binascii.unhexlify(s, ignorechars=ignorechars)

#
Expand Down
5 changes: 4 additions & 1 deletion Lib/importlib/_bootstrap_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@
_MS_WINDOWS = (sys.platform == 'win32')
if _MS_WINDOWS:
import nt as _os
import winreg
try:
import winreg
except ImportError:
winreg = None
else:
import posix as _os

Expand Down
4 changes: 3 additions & 1 deletion Lib/tarfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -2841,9 +2841,11 @@ def makelink_with_filter(self, tarinfo, targetpath,
"makelink_with_filter: if filter_function is not None, "
+ "extraction_root must also not be None")
try:
filter_function(
filtered = filter_function(
unfiltered.replace(name=tarinfo.name, deep=False),
extraction_root)
if filtered is None:
return
filtered = filter_function(unfiltered, extraction_root)
except _FILTER_ERRORS as cause:
raise LinkFallbackError(tarinfo, unfiltered.name) from cause
Expand Down
50 changes: 50 additions & 0 deletions Lib/test/test_asyncio/test_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,33 @@ class FakeCoro:

self.assertEqual(len(result.call_stack), 2)

async def test_stack_wait_for_non_positive_timeout(self):
# gh-157058: wait_for(fut, 0) must still record the waiter
cleanup = asyncio.Future()

async def worker():
try:
await asyncio.Future()
finally:
await cleanup

async def probe(t):
await asyncio.wait_for(t, 0)

t = asyncio.ensure_future(worker())
p = asyncio.create_task(probe(t), name='probe')
for _ in range(5):
await asyncio.sleep(0)

stack = capture_test_stack(fut=t)

cleanup.set_result(None)
await asyncio.gather(p, t, return_exceptions=True)

self.assertEqual(stack[0][2], [
['T<probe>', ['a _cancel_and_wait', 'a wait_for', 'a probe'], []],
])

async def test_stack_gather(self):

stack_for_deep = None
Expand Down Expand Up @@ -202,6 +229,29 @@ async def main():
]
])

async def test_stack_gather_survivor(self):
# gh-157213: a child that outlives gather() must not be shown as awaited

async def fail():
raise ValueError

async def survivor():
await asyncio.Future()

t = asyncio.create_task(survivor(), name='survivor')
with self.assertRaises(ValueError):
await asyncio.gather(t, fail())

self.assertEqual(capture_test_stack(fut=t)[0], [
'T<survivor>',
['a survivor'],
[]
])

t.cancel()
with self.assertRaises(asyncio.CancelledError):
await t

async def test_stack_shield(self):

stack_for_shield = None
Expand Down
11 changes: 9 additions & 2 deletions Lib/test/test_asyncio/test_taskgroups.py
Original file line number Diff line number Diff line change
Expand Up @@ -1200,11 +1200,17 @@ async def child(tg):

async def test_taskgroup_cancel_keeps_outer_cancellation(self):
# gh-155433: any cancellation from outside the group must propagate.
cancelling = asyncio.Event()
release = asyncio.Event()

async def child():
try:
await asyncio.sleep(10)
finally:
await asyncio.sleep(0.1)
# The group is cancelling: it has cancelled its parent task
# and is waiting for this task to finish.
cancelling.set()
await release.wait()

async def body():
async with asyncio.TaskGroup() as tg:
Expand All @@ -1213,8 +1219,9 @@ async def body():
tg.cancel()

task = asyncio.create_task(body())
await asyncio.sleep(0.01)
await cancelling.wait()
task.cancel('message')
release.set()
with self.assertRaises(asyncio.CancelledError) as cm:
await task
self.assertEqual('message', cm.exception.args[0])
Expand Down
43 changes: 43 additions & 0 deletions Lib/test/test_asyncio/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -1231,6 +1231,25 @@ async def coro():

self.loop.run_until_complete(self.new_task(self.loop, coro()))

def test_gather_discards_awaited_by_for_pending(self):
# gh-157213: a child outliving gather() must lose the awaited-by edge
async def fail():
raise ValueError

async def survivor():
await asyncio.Future()

async def coro():
t = self.new_task(self.loop, survivor())
with self.assertRaises(ValueError):
await asyncio.gather(t, fail())
self.assertFalse(t._asyncio_awaited_by)
t.cancel()
with self.assertRaises(asyncio.CancelledError):
await t

self.loop.run_until_complete(self.new_task(self.loop, coro()))

def test_wait_really_done(self):
# there is possibility that some tasks in the pending list
# became done but their callbacks haven't all been called yet
Expand Down Expand Up @@ -1891,6 +1910,30 @@ async def notmuch():
self.loop.run_until_complete(task),
'ko')

def test_step_dont_swallow_systemexit_or_keyboardinterrupt(self):
# see gh-108549: do not swallow SystemExit and KeyboardInterrupt
# in Task.__step when the current task must be cancelled.
async def sub_task(exc):
raise exc

async def current_task(exc):
try:
await asyncio.create_task(sub_task(exc))
except exc:
pass
except BaseException as e:
self.fail(f'{exc} is expected, instead of {type(e)}')
return "ok"

for exc in (SystemExit, KeyboardInterrupt):
with self.subTest(exc):
t = self.new_task(self.loop, current_task(exc))
self.assertRaises(exc, self.loop.run_until_complete, t)
t.cancel()
test_utils.run_briefly(self.loop)
self.assertTrue(not t.cancelled())
self.assertEqual(t.result(), "ok")

def test_step_result_future(self):
# If coroutine returns future, task waits on this future.

Expand Down
40 changes: 28 additions & 12 deletions Lib/test/test_capi/test_complex.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,18 +281,34 @@ def test_py_c_abs(self):
# Test _Py_c_abs()
_py_c_abs = _testcapi._py_c_abs

self.assertEqual(_py_c_abs(-1), (1.0, 0))
self.assertEqual(_py_c_abs(1j), (1.0, 0))

self.assertEqual(_py_c_abs(complex('+inf+1j')), (INF, 0))
self.assertEqual(_py_c_abs(complex('-inf+1j')), (INF, 0))
self.assertEqual(_py_c_abs(complex('1.25+infj')), (INF, 0))
self.assertEqual(_py_c_abs(complex('1.25-infj')), (INF, 0))

self.assertTrue(isnan(_py_c_abs(complex('1.25+nanj'))[0]))
self.assertTrue(isnan(_py_c_abs(complex('nan-1j'))[0]))

self.assertEqual(_py_c_abs(complex(*[DBL_MAX]*2))[1], errno.ERANGE)
def c_abs(num):
# On success, _Py_c_abs() doesn't use errno and leaves errno
# unchanged
_testcapi.set_errno(0)
result, errno = _py_c_abs(num)
self.assertEqual(errno, 0)
return result

try:
self.assertEqual(c_abs(-1), 1.0)
self.assertEqual(c_abs(1j), 1.0)
self.assertEqual(c_abs(complex('+inf+1j')), INF)
self.assertEqual(c_abs(complex('-inf+1j')), INF)
self.assertEqual(c_abs(complex('1.25+infj')), INF)
self.assertEqual(c_abs(complex('1.25-infj')), INF)
self.assertTrue(isnan(c_abs(complex('1.25+nanj'))))
self.assertTrue(isnan(c_abs(complex('nan-1j'))))

# Set errno to ERANGE on overflow
_testcapi.set_errno(0)
self.assertEqual(_py_c_abs(complex(*[DBL_MAX]*2)),
(INF, errno.ERANGE))

# Preserve errno on success
_testcapi.set_errno(errno.EACCES)
self.assertEqual(_py_c_abs(1j), (1.0, errno.EACCES))
finally:
_testcapi.set_errno(0)


if __name__ == "__main__":
Expand Down
Loading
Loading