Skip to content

Commit b994bc7

Browse files
authored
Merge branch '3.14' into backport-0953200-3.14
2 parents f43eaee + 6b26e62 commit b994bc7

27 files changed

+522
-86
lines changed

.devcontainer/devcontainer.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@
55
"dnf",
66
"install",
77
"-y",
8-
"which",
9-
"zsh",
10-
"fish",
118
// For umask fix below.
129
"/usr/bin/setfacl"
1310
],

Doc/library/codecs.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1251,7 +1251,7 @@ particular, the following variants typically exist:
12511251
+-----------------+--------------------------------+--------------------------------+
12521252
| iso8859_3 | iso-8859-3, latin3, L3 | Esperanto, Maltese |
12531253
+-----------------+--------------------------------+--------------------------------+
1254-
| iso8859_4 | iso-8859-4, latin4, L4 | Baltic languages |
1254+
| iso8859_4 | iso-8859-4, latin4, L4 | Northern Europe |
12551255
+-----------------+--------------------------------+--------------------------------+
12561256
| iso8859_5 | iso-8859-5, cyrillic | Belarusian, Bulgarian, |
12571257
| | | Macedonian, Russian, Serbian |

Lib/_pyrepl/utils.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,12 @@ def is_soft_keyword_used(*tokens: TI | None) -> bool:
264264
return True
265265
case (TI(string="case"), TI(string="_"), TI(string=":")):
266266
return True
267+
case (
268+
None | TI(T.NEWLINE) | TI(T.INDENT) | TI(T.DEDENT) | TI(string=":"),
269+
TI(string="type"),
270+
TI(T.NAME, string=s)
271+
):
272+
return not keyword.iskeyword(s)
267273
case _:
268274
return False
269275

Lib/multiprocessing/dummy/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333

3434
class DummyProcess(threading.Thread):
3535

36-
def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):
36+
def __init__(self, group=None, target=None, name=None, args=(), kwargs=None):
3737
threading.Thread.__init__(self, group, target, name, args, kwargs)
3838
self._pid = None
3939
self._children = weakref.WeakKeyDictionary()

Lib/multiprocessing/forkserver.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,13 @@ def ensure_running(self):
145145
cmd = ('from multiprocessing.forkserver import main; ' +
146146
'main(%d, %d, %r, **%r)')
147147

148+
main_kws = {}
148149
if self._preload_modules:
149-
desired_keys = {'main_path', 'sys_path'}
150150
data = spawn.get_preparation_data('ignore')
151-
main_kws = {x: y for x, y in data.items() if x in desired_keys}
152-
else:
153-
main_kws = {}
151+
if 'sys_path' in data:
152+
main_kws['sys_path'] = data['sys_path']
153+
if 'init_main_from_path' in data:
154+
main_kws['main_path'] = data['init_main_from_path']
154155

155156
with socket.socket(socket.AF_UNIX) as listener:
156157
address = connection.arbitrary_address('AF_UNIX')

Lib/multiprocessing/process.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ class BaseProcess(object):
7777
def _Popen(self):
7878
raise NotImplementedError
7979

80-
def __init__(self, group=None, target=None, name=None, args=(), kwargs={},
80+
def __init__(self, group=None, target=None, name=None, args=(), kwargs=None,
8181
*, daemon=None):
8282
assert group is None, 'group argument must be None for now'
8383
count = next(_process_counter)
@@ -89,7 +89,7 @@ def __init__(self, group=None, target=None, name=None, args=(), kwargs={},
8989
self._closed = False
9090
self._target = target
9191
self._args = tuple(args)
92-
self._kwargs = dict(kwargs)
92+
self._kwargs = dict(kwargs) if kwargs else {}
9393
self._name = name or type(self).__name__ + '-' + \
9494
':'.join(str(i) for i in self._identity)
9595
if daemon is not None:

Lib/test/_test_multiprocessing.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5173,6 +5173,23 @@ def test_invalid_handles(self):
51735173
multiprocessing.connection.Connection, -1)
51745174

51755175

5176+
#
5177+
# Regression tests for BaseProcess kwargs handling
5178+
#
5179+
5180+
class TestBaseProcessKwargs(unittest.TestCase):
5181+
def test_default_kwargs_not_shared_between_instances(self):
5182+
# Creating multiple Process instances without passing kwargs
5183+
# must create independent empty dicts (no shared state).
5184+
p1 = multiprocessing.Process(target=lambda: None)
5185+
p2 = multiprocessing.Process(target=lambda: None)
5186+
self.assertIsInstance(p1._kwargs, dict)
5187+
self.assertIsInstance(p2._kwargs, dict)
5188+
self.assertIsNot(p1._kwargs, p2._kwargs)
5189+
# Mutating one should not affect the other
5190+
p1._kwargs['x'] = 1
5191+
self.assertNotIn('x', p2._kwargs)
5192+
51765193

51775194
@hashlib_helper.requires_hashdigest('sha256')
51785195
class OtherTest(unittest.TestCase):
@@ -6883,6 +6900,18 @@ def child():
68836900
self.assertEqual(q.get_nowait(), "done")
68846901
close_queue(q)
68856902

6903+
def test_preload_main(self):
6904+
# gh-126631: Check that __main__ can be pre-loaded
6905+
if multiprocessing.get_start_method() != "forkserver":
6906+
self.skipTest("forkserver specific test")
6907+
6908+
name = os.path.join(os.path.dirname(__file__), 'mp_preload_main.py')
6909+
_, out, err = test.support.script_helper.assert_python_ok(name)
6910+
self.assertEqual(err, b'')
6911+
6912+
# The trailing empty string comes from split() on output ending with \n
6913+
out = out.decode().split("\n")
6914+
self.assertEqual(out, ['__main__', '__mp_main__', 'f', 'f', ''])
68866915

68876916
#
68886917
# Mixins

Lib/test/mp_preload_main.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import multiprocessing
2+
3+
print(f"{__name__}")
4+
5+
def f():
6+
print("f")
7+
8+
if __name__ == "__main__":
9+
ctx = multiprocessing.get_context("forkserver")
10+
ctx.set_forkserver_preload(['__main__'])
11+
for _ in range(2):
12+
p = ctx.Process(target=f)
13+
p.start()
14+
p.join()

Lib/test/test_genericalias.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,10 @@ def __deepcopy__(self, memo):
402402
aliases = [
403403
GenericAlias(list, T),
404404
GenericAlias(deque, T),
405-
GenericAlias(X, T)
405+
GenericAlias(X, T),
406+
X[T],
407+
list[T],
408+
deque[T],
406409
] + _UNPACKED_TUPLES
407410
for alias in aliases:
408411
with self.subTest(alias=alias):
@@ -432,10 +435,26 @@ def test_union_generic(self):
432435
self.assertEqual(a.__parameters__, (T,))
433436

434437
def test_dir(self):
435-
dir_of_gen_alias = set(dir(list[int]))
438+
ga = list[int]
439+
dir_of_gen_alias = set(dir(ga))
436440
self.assertTrue(dir_of_gen_alias.issuperset(dir(list)))
437-
for generic_alias_property in ("__origin__", "__args__", "__parameters__"):
438-
self.assertIn(generic_alias_property, dir_of_gen_alias)
441+
for generic_alias_property in (
442+
"__origin__", "__args__", "__parameters__",
443+
"__unpacked__",
444+
):
445+
with self.subTest(generic_alias_property=generic_alias_property):
446+
self.assertIn(generic_alias_property, dir_of_gen_alias)
447+
for blocked in (
448+
"__bases__",
449+
"__copy__",
450+
"__deepcopy__",
451+
):
452+
with self.subTest(blocked=blocked):
453+
self.assertNotIn(blocked, dir_of_gen_alias)
454+
455+
for entry in dir_of_gen_alias:
456+
with self.subTest(entry=entry):
457+
getattr(ga, entry) # must not raise `AttributeError`
439458

440459
def test_weakref(self):
441460
for t in self.generic_types:

Lib/test/test_inspect/test_inspect.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1755,14 +1755,55 @@ class C(metaclass=M):
17551755

17561756
class TestFormatAnnotation(unittest.TestCase):
17571757
def test_typing_replacement(self):
1758-
from test.typinganndata.ann_module9 import ann, ann1
1758+
from test.typinganndata.ann_module9 import A, ann, ann1
17591759
self.assertEqual(inspect.formatannotation(ann), 'List[str] | int')
17601760
self.assertEqual(inspect.formatannotation(ann1), 'List[testModule.typing.A] | int')
17611761

1762+
self.assertEqual(inspect.formatannotation(A, 'testModule.typing'), 'A')
1763+
self.assertEqual(inspect.formatannotation(A, 'other'), 'testModule.typing.A')
1764+
self.assertEqual(
1765+
inspect.formatannotation(ann1, 'testModule.typing'),
1766+
'List[testModule.typing.A] | int',
1767+
)
1768+
17621769
def test_forwardref(self):
17631770
fwdref = ForwardRef('fwdref')
17641771
self.assertEqual(inspect.formatannotation(fwdref), 'fwdref')
17651772

1773+
def test_formatannotationrelativeto(self):
1774+
from test.typinganndata.ann_module9 import A, ann1
1775+
1776+
# Builtin types:
1777+
self.assertEqual(
1778+
inspect.formatannotationrelativeto(object)(type),
1779+
'type',
1780+
)
1781+
1782+
# Custom types:
1783+
self.assertEqual(
1784+
inspect.formatannotationrelativeto(None)(A),
1785+
'testModule.typing.A',
1786+
)
1787+
1788+
class B: ...
1789+
B.__module__ = 'testModule.typing'
1790+
1791+
self.assertEqual(
1792+
inspect.formatannotationrelativeto(B)(A),
1793+
'A',
1794+
)
1795+
1796+
self.assertEqual(
1797+
inspect.formatannotationrelativeto(object)(A),
1798+
'testModule.typing.A',
1799+
)
1800+
1801+
# Not an instance of "type":
1802+
self.assertEqual(
1803+
inspect.formatannotationrelativeto(A)(ann1),
1804+
'List[testModule.typing.A] | int',
1805+
)
1806+
17661807

17671808
class TestIsMethodDescriptor(unittest.TestCase):
17681809

0 commit comments

Comments
 (0)