Skip to content

Commit aa7c603

Browse files
committed
Merge branch 'main' into tstrings
2 parents 051e8eb + 4b4b9fb commit aa7c603

26 files changed

+727
-119
lines changed

Doc/library/struct.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,9 +273,9 @@ C11 standard) is supported, the following format characters are available:
273273
+--------+--------------------------+--------------------+----------------+------------+
274274
| Format | C Type | Python type | Standard size | Notes |
275275
+========+==========================+====================+================+============+
276-
| ``E`` | :c:expr:`float complex` | complex | 8 | \(10) |
276+
| ``F`` | :c:expr:`float complex` | complex | 8 | \(10) |
277277
+--------+--------------------------+--------------------+----------------+------------+
278-
| ``C`` | :c:expr:`double complex` | complex | 16 | \(10) |
278+
| ``D`` | :c:expr:`double complex` | complex | 16 | \(10) |
279279
+--------+--------------------------+--------------------+----------------+------------+
280280

281281
.. versionchanged:: 3.3
@@ -285,7 +285,7 @@ C11 standard) is supported, the following format characters are available:
285285
Added support for the ``'e'`` format.
286286

287287
.. versionchanged:: 3.14
288-
Added support for the ``'E'`` and ``'C'`` formats.
288+
Added support for the ``'F'`` and ``'D'`` formats.
289289

290290

291291
Notes:

Doc/whatsnew/3.14.rst

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,49 @@ is unchanged.
335335
Improved error messages
336336
-----------------------
337337

338+
* The interpreter now provides helpful suggestions when it detects typos in Python
339+
keywords. When a word that closely resembles a Python keyword is encountered,
340+
the interpreter will suggest the correct keyword in the error message. This
341+
feature helps programmers quickly identify and fix common typing mistakes. For
342+
example:
343+
344+
.. code-block:: python
345+
346+
>>> whille True:
347+
... pass
348+
Traceback (most recent call last):
349+
File "<stdin>", line 1
350+
whille True:
351+
^^^^^^
352+
SyntaxError: invalid syntax. Did you mean 'while'?
353+
354+
>>> asynch def fetch_data():
355+
... pass
356+
Traceback (most recent call last):
357+
File "<stdin>", line 1
358+
asynch def fetch_data():
359+
^^^^^^
360+
SyntaxError: invalid syntax. Did you mean 'async'?
361+
362+
>>> async def foo():
363+
... awaid fetch_data()
364+
Traceback (most recent call last):
365+
File "<stdin>", line 2
366+
awaid fetch_data()
367+
^^^^^
368+
SyntaxError: invalid syntax. Did you mean 'await'?
369+
370+
>>> raisee ValueError("Error")
371+
Traceback (most recent call last):
372+
File "<stdin>", line 1
373+
raisee ValueError("Error")
374+
^^^^^^
375+
SyntaxError: invalid syntax. Did you mean 'raise'?
376+
377+
While the feature focuses on the most common cases, some variations of
378+
misspellings may still result in regular syntax errors.
379+
(Contributed by Pablo Galindo in :gh:`132449`.)
380+
338381
* When unpacking assignment fails due to incorrect number of variables, the
339382
error message prints the received number of values in more cases than before.
340383
(Contributed by Tushar Sadhwani in :gh:`122239`.)
@@ -1201,7 +1244,7 @@ struct
12011244
------
12021245

12031246
* Support the :c:expr:`float complex` and :c:expr:`double complex` C types in
1204-
the :mod:`struct` module (formatting characters ``'E'`` and ``'C'``,
1247+
the :mod:`struct` module (formatting characters ``'F'`` and ``'D'``,
12051248
respectively) if the compiler has C11 complex arithmetic.
12061249
(Contributed by Sergey B Kirpichev in :gh:`121249`.)
12071250

Lib/ctypes/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,13 +209,13 @@ class c_longdouble(_SimpleCData):
209209

210210
try:
211211
class c_double_complex(_SimpleCData):
212-
_type_ = "C"
212+
_type_ = "D"
213213
_check_size(c_double_complex)
214214
class c_float_complex(_SimpleCData):
215-
_type_ = "E"
215+
_type_ = "F"
216216
_check_size(c_float_complex)
217217
class c_longdouble_complex(_SimpleCData):
218-
_type_ = "F"
218+
_type_ = "G"
219219
except AttributeError:
220220
pass
221221

Lib/test/_test_multiprocessing.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,6 +1516,7 @@ def _test_lock_locked_2processes(cls, lock, event, res):
15161516
res.value = lock.locked()
15171517
event.set()
15181518

1519+
@unittest.skipUnless(HAS_SHAREDCTYPES, 'needs sharedctypes')
15191520
def test_lock_locked_2processes(self):
15201521
if self.TYPE != 'processes':
15211522
self.skipTest('test not appropriate for {}'.format(self.TYPE))
@@ -1600,6 +1601,7 @@ def test_rlock(self):
16001601
self.assertFalse(lock.locked())
16011602
self.assertRaises((AssertionError, RuntimeError), lock.release)
16021603

1604+
@unittest.skipUnless(HAS_SHAREDCTYPES, 'needs sharedctypes')
16031605
def test_rlock_locked_2processes(self):
16041606
if self.TYPE != 'processes':
16051607
self.skipTest('test not appropriate for {}'.format(self.TYPE))

Lib/test/test_capi/test_abstract.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,8 @@ def test_mapping_haskey(self):
460460
self.assertFalse(haskey({}, []))
461461
self.assertEqual(cm.unraisable.exc_type, TypeError)
462462
self.assertEqual(str(cm.unraisable.exc_value),
463-
"unhashable type: 'list'")
463+
"cannot use 'list' as a dict key "
464+
"(unhashable type: 'list')")
464465

465466
with support.catch_unraisable_exception() as cm:
466467
self.assertFalse(haskey([], 1))

Lib/test/test_ctypes/test_c_simple_type_meta.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,11 @@ def test_bad_type_message(self):
160160
class F(metaclass=PyCSimpleType):
161161
_type_ = "\0"
162162
message = str(cm.exception)
163-
expected_type_chars = list('cbBhHiIlLdCEFfuzZqQPXOv?g')
163+
expected_type_chars = list('cbBhHiIlLdDFGfuzZqQPXOv?g')
164164
if not hasattr(ctypes, 'c_float_complex'):
165-
expected_type_chars.remove('C')
166-
expected_type_chars.remove('E')
167165
expected_type_chars.remove('F')
166+
expected_type_chars.remove('D')
167+
expected_type_chars.remove('G')
168168
if not MS_WINDOWS:
169169
expected_type_chars.remove('X')
170170
self.assertIn("'" + ''.join(expected_type_chars) + "'", message)

Lib/test/test_dict.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import gc
44
import pickle
55
import random
6+
import re
67
import string
78
import sys
89
import unittest
@@ -1487,6 +1488,47 @@ def make_pairs():
14871488
self.assertEqual(d.get(key3_3), 44)
14881489
self.assertGreaterEqual(eq_count, 1)
14891490

1491+
def test_unhashable_key(self):
1492+
d = {'a': 1}
1493+
key = [1, 2, 3]
1494+
1495+
def check_unhashable_key():
1496+
msg = "cannot use 'list' as a dict key (unhashable type: 'list')"
1497+
return self.assertRaisesRegex(TypeError, re.escape(msg))
1498+
1499+
with check_unhashable_key():
1500+
key in d
1501+
with check_unhashable_key():
1502+
d[key]
1503+
with check_unhashable_key():
1504+
d[key] = 2
1505+
with check_unhashable_key():
1506+
d.setdefault(key, 2)
1507+
with check_unhashable_key():
1508+
d.pop(key)
1509+
with check_unhashable_key():
1510+
d.get(key)
1511+
1512+
# Only TypeError exception is overriden,
1513+
# other exceptions are left unchanged.
1514+
class HashError:
1515+
def __hash__(self):
1516+
raise KeyError('error')
1517+
1518+
key2 = HashError()
1519+
with self.assertRaises(KeyError):
1520+
key2 in d
1521+
with self.assertRaises(KeyError):
1522+
d[key2]
1523+
with self.assertRaises(KeyError):
1524+
d[key2] = 2
1525+
with self.assertRaises(KeyError):
1526+
d.setdefault(key2, 2)
1527+
with self.assertRaises(KeyError):
1528+
d.pop(key2)
1529+
with self.assertRaises(KeyError):
1530+
d.get(key2)
1531+
14901532

14911533
class CAPITest(unittest.TestCase):
14921534

Lib/test/test_external_inspection.py

Lines changed: 122 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import textwrap
44
import importlib
55
import sys
6-
from test.support import os_helper, SHORT_TIMEOUT
6+
from test.support import os_helper, SHORT_TIMEOUT, busy_retry
77
from test.support.script_helper import make_script
88

99
import subprocess
@@ -14,6 +14,7 @@
1414
from _testexternalinspection import PROCESS_VM_READV_SUPPORTED
1515
from _testexternalinspection import get_stack_trace
1616
from _testexternalinspection import get_async_stack_trace
17+
from _testexternalinspection import get_all_awaited_by
1718
except ImportError:
1819
raise unittest.SkipTest(
1920
"Test only runs when _testexternalinspection is available")
@@ -349,6 +350,126 @@ async def main():
349350
]
350351
self.assertEqual(stack_trace, expected_stack_trace)
351352

353+
@unittest.skipIf(sys.platform != "darwin" and sys.platform != "linux",
354+
"Test only runs on Linux and MacOS")
355+
@unittest.skipIf(sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED,
356+
"Test only runs on Linux with process_vm_readv support")
357+
def test_async_global_awaited_by(self):
358+
script = textwrap.dedent("""\
359+
import asyncio
360+
import os
361+
import random
362+
import sys
363+
from string import ascii_lowercase, digits
364+
from test.support import socket_helper, SHORT_TIMEOUT
365+
366+
HOST = '127.0.0.1'
367+
PORT = socket_helper.find_unused_port()
368+
connections = 0
369+
370+
class EchoServerProtocol(asyncio.Protocol):
371+
def connection_made(self, transport):
372+
global connections
373+
connections += 1
374+
self.transport = transport
375+
376+
def data_received(self, data):
377+
self.transport.write(data)
378+
self.transport.close()
379+
380+
async def echo_client(message):
381+
reader, writer = await asyncio.open_connection(HOST, PORT)
382+
writer.write(message.encode())
383+
await writer.drain()
384+
385+
data = await reader.read(100)
386+
assert message == data.decode()
387+
writer.close()
388+
await writer.wait_closed()
389+
await asyncio.sleep(SHORT_TIMEOUT)
390+
391+
async def echo_client_spam(server):
392+
async with asyncio.TaskGroup() as tg:
393+
while connections < 1000:
394+
msg = list(ascii_lowercase + digits)
395+
random.shuffle(msg)
396+
tg.create_task(echo_client("".join(msg)))
397+
await asyncio.sleep(0)
398+
# at least a 1000 tasks created
399+
fifo_path = sys.argv[1]
400+
with open(fifo_path, "w") as fifo:
401+
fifo.write("ready")
402+
# at this point all client tasks completed without assertion errors
403+
# let's wrap up the test
404+
server.close()
405+
await server.wait_closed()
406+
407+
async def main():
408+
loop = asyncio.get_running_loop()
409+
server = await loop.create_server(EchoServerProtocol, HOST, PORT)
410+
async with server:
411+
async with asyncio.TaskGroup() as tg:
412+
tg.create_task(server.serve_forever(), name="server task")
413+
tg.create_task(echo_client_spam(server), name="echo client spam")
414+
415+
asyncio.run(main())
416+
""")
417+
stack_trace = None
418+
with os_helper.temp_dir() as work_dir:
419+
script_dir = os.path.join(work_dir, "script_pkg")
420+
os.mkdir(script_dir)
421+
fifo = f"{work_dir}/the_fifo"
422+
os.mkfifo(fifo)
423+
script_name = _make_test_script(script_dir, 'script', script)
424+
try:
425+
p = subprocess.Popen([sys.executable, script_name, str(fifo)])
426+
with open(fifo, "r") as fifo_file:
427+
response = fifo_file.read()
428+
self.assertEqual(response, "ready")
429+
for _ in busy_retry(SHORT_TIMEOUT):
430+
try:
431+
all_awaited_by = get_all_awaited_by(p.pid)
432+
except RuntimeError as re:
433+
# This call reads a linked list in another process with
434+
# no synchronization. That occasionally leads to invalid
435+
# reads. Here we avoid making the test flaky.
436+
msg = str(re)
437+
if msg.startswith("Task list appears corrupted"):
438+
continue
439+
elif msg.startswith("Invalid linked list structure reading remote memory"):
440+
continue
441+
elif msg.startswith("Unknown error reading memory"):
442+
continue
443+
elif msg.startswith("Unhandled frame owner"):
444+
continue
445+
raise # Unrecognized exception, safest not to ignore it
446+
else:
447+
break
448+
# expected: a list of two elements: 1 thread, 1 interp
449+
self.assertEqual(len(all_awaited_by), 2)
450+
# expected: a tuple with the thread ID and the awaited_by list
451+
self.assertEqual(len(all_awaited_by[0]), 2)
452+
# expected: no tasks in the fallback per-interp task list
453+
self.assertEqual(all_awaited_by[1], (0, []))
454+
entries = all_awaited_by[0][1]
455+
# expected: at least 1000 pending tasks
456+
self.assertGreaterEqual(len(entries), 1000)
457+
# the first three tasks stem from the code structure
458+
self.assertIn(('Task-1', []), entries)
459+
self.assertIn(('server task', [[['main'], 'Task-1', []]]), entries)
460+
self.assertIn(('echo client spam', [[['main'], 'Task-1', []]]), entries)
461+
# the final task will have some random number, but it should for
462+
# sure be one of the echo client spam horde
463+
self.assertEqual([[['echo_client_spam'], 'echo client spam', [[['main'], 'Task-1', []]]]], entries[-1][1])
464+
except PermissionError:
465+
self.skipTest(
466+
"Insufficient permissions to read the stack trace")
467+
finally:
468+
os.remove(fifo)
469+
p.kill()
470+
p.terminate()
471+
p.wait(timeout=SHORT_TIMEOUT)
472+
352473
@unittest.skipIf(sys.platform != "darwin" and sys.platform != "linux",
353474
"Test only runs on Linux and MacOS")
354475
@unittest.skipIf(sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED,

Lib/test/test_import/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,7 +1055,7 @@ class substr(str):
10551055
""")
10561056
popen = script_helper.spawn_python("main.py", cwd=tmp)
10571057
stdout, stderr = popen.communicate()
1058-
self.assertEqual(stdout.rstrip(), b"unhashable type: 'substr'")
1058+
self.assertIn(b"unhashable type: 'substr'", stdout.rstrip())
10591059

10601060
with open(os.path.join(tmp, "main.py"), "w", encoding='utf-8') as f:
10611061
f.write("""
@@ -1072,7 +1072,7 @@ class substr(str):
10721072

10731073
popen = script_helper.spawn_python("main.py", cwd=tmp)
10741074
stdout, stderr = popen.communicate()
1075-
self.assertEqual(stdout.rstrip(), b"unhashable type: 'substr'")
1075+
self.assertIn(b"unhashable type: 'substr'", stdout.rstrip())
10761076

10771077
# Various issues with sys module
10781078
with open(os.path.join(tmp, "main.py"), "w", encoding='utf-8') as f:

0 commit comments

Comments
 (0)