Skip to content
4 changes: 2 additions & 2 deletions Doc/library/annotationlib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -535,9 +535,9 @@ following attributes:
:attr:`~Format.VALUE_WITH_FAKE_GLOBALS`.
* A :ref:`code object <code-objects>` ``__code__`` containing the compiled code for the
annotate function.
* Optional: A tuple of the function's positional defaults ``__kwdefaults__``, if the
* Optional: A tuple of the function's positional defaults ``__defaults__``, if the
function represented by ``__code__`` uses any positional defaults.
* Optional: A dict of the function's keyword defaults ``__defaults__``, if the function
* Optional: A dict of the function's keyword defaults ``__kwdefaults__``, if the function
represented by ``__code__`` uses any keyword defaults.
* Optional: All other :ref:`function attributes <inspect-types>`.

Expand Down
9 changes: 6 additions & 3 deletions Lib/test/libregrtest/run_workers.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,12 @@ def _kill(self) -> None:

use_killpg = USE_PROCESS_GROUP
if use_killpg:
parent_sid = os.getsid(0)
sid = os.getsid(popen.pid)
use_killpg = (sid != parent_sid)
try:
use_killpg = (os.getsid(popen.pid) != os.getsid(0))
except PermissionError:
# On OpenBSD getsid() is only allowed for a process in the
# same session, so the failure means that it is not.
use_killpg = True

if use_killpg:
what = f"{self} process group"
Expand Down
7 changes: 6 additions & 1 deletion Lib/test/test_asyncio/test_sendfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,12 @@ class SendfileBase:
# 64 KiB page configuration.
DATA = b"x" * (1024 * 17 * 64 + 1)
# Reduce socket buffer size to test on relative small data sets.
BUF_SIZE = 4 * 1024 # 4 KiB
if sys.platform.startswith('dragonfly'):
# A smaller buffer makes every window update wait 100 ms for the
# delayed ACK timer.
BUF_SIZE = 32 * 1024 # 32 KiB
else:
BUF_SIZE = 4 * 1024 # 4 KiB

def create_event_loop(self):
raise NotImplementedError
Expand Down
7 changes: 5 additions & 2 deletions Lib/test/test_epoll.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import os
import select
import socket
import sys
import time
import unittest
from test import support
Expand Down Expand Up @@ -52,12 +53,11 @@ def tearDown(self):
def _connected_pair(self):
client = socket.socket()
client.setblocking(False)
# The connection is either established immediately or in progress.
try:
client.connect(('127.0.0.1', self.serverSocket.getsockname()[1]))
except OSError as e:
self.assertEqual(e.args[0], errno.EINPROGRESS)
else:
raise AssertionError("Connect should have raised EINPROGRESS")
server, addr = self.serverSocket.accept()

self.connections.extend((client, server))
Expand Down Expand Up @@ -218,6 +218,9 @@ def test_errors(self):
self.assertRaises(ValueError, select.epoll().register, -1,
select.EPOLLIN)

@unittest.skipIf(sys.platform.startswith('sunos'),
'unregistering a closed file descriptor succeeds '
'on Solaris')
def test_unregister_closed(self):
server, client = self._connected_pair()
fd = server.fileno()
Expand Down
20 changes: 20 additions & 0 deletions Lib/test/test_free_threading/test_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,5 +336,25 @@ def reader():
with threading_helper.start_threads([t1, t2]):
pass

def test_getstate_race_with_shared_keys(self):
box = [None]
enter = Barrier(2)
leave = Barrier(2)

def reader():
for _ in range(1000):
enter.wait()
box[0].__getstate__()
leave.wait()

def writer():
for i in range(1000):
box[0] = type(f"C{i}", (), {})()
enter.wait()
setattr(box[0], str(i), 1)
leave.wait()

threading_helper.run_concurrently([reader, writer])

if __name__ == "__main__":
unittest.main()
20 changes: 20 additions & 0 deletions Lib/test/test_json/test_decode.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,26 @@ def test_empty_objects(self):
self.assertEqual(self.loads('[]'), [])
self.assertEqual(self.loads('""'), "")

def test_object_hook(self):
s = '{"a":{"b":{}}}'

expected_result = {"a":{"b":{"x":1}, "x":1}, "x":1}
expected_hook_arguments = [
{}, {"b": {"x":1}}, {"a": {"b": {"x":1}, "x":1}}
]

hook_arguments = []

def hook(x):
hook_arguments.append(x)
return {**x, "x":1}

result = self.loads(s, object_hook=hook)

self.assertEqual(result, expected_result)
self.assertEqual(hook_arguments, expected_hook_arguments)


def test_object_pairs_hook(self):
s = '{"xkd":1, "kcw":2, "art":3, "hxm":4, "qrt":5, "pad":6, "hoy":7}'
p = [("xkd", 1), ("kcw", 2), ("art", 3), ("hxm", 4),
Expand Down
14 changes: 14 additions & 0 deletions Lib/test/test_json/test_pickleable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import json
import pickle
import unittest

class TestPickleable(unittest.TestCase):
def test_json_decode_error_is_pickleable(self):
e = json.JSONDecodeError(msg="abc", doc="def", pos=7)

pickled = pickle.dumps(e)
unpickled = pickle.loads(pickled)

self.assertEqual(unpickled.msg, e.msg)
self.assertEqual(unpickled.doc, e.doc)
self.assertEqual(unpickled.pos, e.pos)
19 changes: 17 additions & 2 deletions Lib/test/test_os/test_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -4728,12 +4728,27 @@ def test_posix_pty_functions(self):
son_fd = os.open(son_path, os.O_RDWR|os.O_NOCTTY)
self.addCleanup(os.close, son_fd)
if sys.platform.startswith('sunos'):
# The slave is not a terminal until these STREAMS modules
# are pushed onto it.
import fcntl
I_PUSH = 0x5302
TIOCNOTTY = 0x7471
# Pushing "ptem" makes the slave a terminal, which a session
# leader without a controlling terminal then acquires as one
# despite O_NOCTTY. Note whether we already had one.
try:
os.close(os.open('/dev/tty', os.O_RDONLY|os.O_NOCTTY))
had_ctty = True
except OSError:
had_ctty = False
fcntl.ioctl(son_fd, I_PUSH, b'ptem\0')
fcntl.ioctl(son_fd, I_PUSH, b'ldterm\0')
if not had_ctty and os.getsid(0) == os.getpid():
# Disown it, otherwise closing the file descriptors sends
# SIGHUP to the session. TIOCNOTTY sends it too.
old_handler = signal.signal(signal.SIGHUP, signal.SIG_IGN)
try:
fcntl.ioctl(son_fd, TIOCNOTTY)
finally:
signal.signal(signal.SIGHUP, old_handler)
self.assertEqual(os.ptsname(mother_fd), os.ttyname(son_fd))

@warnings_helper.ignore_fork_in_thread_deprecation_warnings()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix potential data race when calling :meth:`~object.__getstate__`
under the :term:`free-threaded build`.
2 changes: 1 addition & 1 deletion Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -7722,7 +7722,7 @@ _PyObject_IsInstanceDictEmpty(PyObject *obj)
PyDictValues *values = _PyObject_InlineValues(obj);
if (FT_ATOMIC_LOAD_UINT8(values->valid)) {
PyDictKeysObject *keys = CACHED_KEYS(tp);
for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) {
for (Py_ssize_t i = 0; i < LOAD_KEYS_NENTRIES(keys); i++) {
if (FT_ATOMIC_LOAD_PTR_RELAXED(values->values[i]) != NULL) {
return 0;
}
Expand Down
Loading