Skip to content

Commit 4dc97c9

Browse files
authored
Merge branch 'main' into small_int_immortal
2 parents 0cd1eba + 46f8a7b commit 4dc97c9

File tree

12 files changed

+308
-97
lines changed

12 files changed

+308
-97
lines changed

Lib/test/support/strace_helper.py

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,27 @@ def sections(self):
7171

7272
return sections
7373

74+
def _filter_memory_call(call):
75+
# mmap can operate on a fd or "MAP_ANONYMOUS" which gives a block of memory.
76+
# Ignore "MAP_ANONYMOUS + the "MAP_ANON" alias.
77+
if call.syscall == "mmap" and "MAP_ANON" in call.args[3]:
78+
return True
79+
80+
if call.syscall in ("munmap", "mprotect"):
81+
return True
82+
83+
return False
84+
85+
86+
def filter_memory(syscalls):
87+
"""Filter out memory allocation calls from File I/O calls.
88+
89+
Some calls (mmap, munmap, etc) can be used on files or to just get a block
90+
of memory. Use this function to filter out the memory related calls from
91+
other calls."""
92+
93+
return [call for call in syscalls if not _filter_memory_call(call)]
94+
7495

7596
@support.requires_subprocess()
7697
def strace_python(code, strace_flags, check=True):
@@ -93,8 +114,6 @@ def _make_error(reason, details):
93114
"-c",
94115
textwrap.dedent(code),
95116
__run_using_command=[_strace_binary] + strace_flags,
96-
# Don't want to trace our JIT's own mmap and mprotect calls:
97-
PYTHON_JIT="0",
98117
)
99118
except OSError as err:
100119
return _make_error("Caught OSError", err)
@@ -145,9 +164,14 @@ def get_events(code, strace_flags, prelude, cleanup):
145164
return all_sections['code']
146165

147166

148-
def get_syscalls(code, strace_flags, prelude="", cleanup=""):
167+
def get_syscalls(code, strace_flags, prelude="", cleanup="",
168+
ignore_memory=True):
149169
"""Get the syscalls which a given chunk of python code generates"""
150170
events = get_events(code, strace_flags, prelude=prelude, cleanup=cleanup)
171+
172+
if ignore_memory:
173+
events = filter_memory(events)
174+
151175
return [ev.syscall for ev in events]
152176

153177

@@ -177,5 +201,5 @@ def requires_strace():
177201
return unittest.skipUnless(_can_strace(), "Requires working strace")
178202

179203

180-
__all__ = ["get_events", "get_syscalls", "requires_strace", "strace_python",
181-
"StraceEvent", "StraceResult"]
204+
__all__ = ["filter_memory", "get_events", "get_syscalls", "requires_strace",
205+
"strace_python", "StraceEvent", "StraceResult"]

Lib/test/test_fileio.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -364,8 +364,7 @@ def testErrnoOnClosedReadinto(self, f):
364364

365365
@strace_helper.requires_strace()
366366
def test_syscalls_read(self):
367-
"""Check that the set of system calls produced by the I/O stack is what
368-
is expected for various read cases.
367+
"""Check set of system calls during common I/O patterns
369368
370369
It's expected as bits of the I/O implementation change, this will need
371370
to change. The goal is to catch changes that unintentionally add
@@ -383,6 +382,11 @@ def check_readall(name, code, prelude="", cleanup="",
383382
prelude=prelude,
384383
cleanup=cleanup)
385384

385+
# Some system calls (ex. mmap) can be used for both File I/O and
386+
# memory allocation. Filter out the ones used for memory
387+
# allocation.
388+
syscalls = strace_helper.filter_memory(syscalls)
389+
386390
# The first call should be an open that returns a
387391
# file descriptor (fd). Afer that calls may vary. Once the file
388392
# is opened, check calls refer to it by fd as the filename

Lib/test/test_tools/i18n_data/messages.pot

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,53 +15,75 @@ msgstr ""
1515
"Generated-By: pygettext.py 1.5\n"
1616

1717

18-
#: messages.py:5
18+
#: messages.py:16
1919
msgid ""
2020
msgstr ""
2121

22-
#: messages.py:8 messages.py:9
22+
#: messages.py:19 messages.py:20
2323
msgid "parentheses"
2424
msgstr ""
2525

26-
#: messages.py:12
26+
#: messages.py:23
2727
msgid "Hello, world!"
2828
msgstr ""
2929

30-
#: messages.py:15
30+
#: messages.py:26
3131
msgid ""
3232
"Hello,\n"
3333
" multiline!\n"
3434
msgstr ""
3535

36-
#: messages.py:29
36+
#: messages.py:46 messages.py:89 messages.py:90 messages.py:93 messages.py:94
37+
#: messages.py:99
38+
msgid "foo"
39+
msgid_plural "foos"
40+
msgstr[0] ""
41+
msgstr[1] ""
42+
43+
#: messages.py:47
44+
msgid "something"
45+
msgstr ""
46+
47+
#: messages.py:50
3748
msgid "Hello, {}!"
3849
msgstr ""
3950

40-
#: messages.py:33
51+
#: messages.py:54
4152
msgid "1"
4253
msgstr ""
4354

44-
#: messages.py:33
55+
#: messages.py:54
4556
msgid "2"
4657
msgstr ""
4758

48-
#: messages.py:34 messages.py:35
59+
#: messages.py:55 messages.py:56
4960
msgid "A"
5061
msgstr ""
5162

52-
#: messages.py:34 messages.py:35
63+
#: messages.py:55 messages.py:56
5364
msgid "B"
5465
msgstr ""
5566

56-
#: messages.py:36
67+
#: messages.py:57
5768
msgid "set"
5869
msgstr ""
5970

60-
#: messages.py:42
71+
#: messages.py:63
6172
msgid "nested string"
6273
msgstr ""
6374

64-
#: messages.py:47
75+
#: messages.py:68
6576
msgid "baz"
6677
msgstr ""
6778

79+
#: messages.py:91 messages.py:92 messages.py:95 messages.py:96
80+
msgctxt "context"
81+
msgid "foo"
82+
msgid_plural "foos"
83+
msgstr[0] ""
84+
msgstr[1] ""
85+
86+
#: messages.py:100
87+
msgid "domain foo"
88+
msgstr ""
89+

Lib/test/test_tools/i18n_data/messages.py

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# Test message extraction
2-
from gettext import gettext as _
2+
from gettext import (
3+
gettext,
4+
ngettext,
5+
pgettext,
6+
npgettext,
7+
dgettext,
8+
dngettext,
9+
dpgettext,
10+
dnpgettext
11+
)
12+
13+
_ = gettext
314

415
# Empty string
516
_("")
@@ -21,13 +32,23 @@
2132
_(None)
2233
_(1)
2334
_(False)
24-
_(x="kwargs are not allowed")
35+
_(("invalid"))
36+
_(["invalid"])
37+
_({"invalid"})
38+
_("string"[3])
39+
_("string"[:3])
40+
_({"string": "foo"})
41+
42+
# pygettext does not allow keyword arguments, but both xgettext and pybabel do
43+
_(x="kwargs work!")
44+
45+
# Unusual, but valid arguments
2546
_("foo", "bar")
2647
_("something", x="something else")
2748

2849
# .format()
2950
_("Hello, {}!").format("world") # valid
30-
_("Hello, {}!".format("world")) # invalid
51+
_("Hello, {}!".format("world")) # invalid, but xgettext and pybabel extract the first string
3152

3253
# Nested structures
3354
_("1"), _("2")
@@ -62,3 +83,28 @@ def _(x):
6283

6384
def _(x="don't extract me"):
6485
pass
86+
87+
88+
# Other gettext functions
89+
gettext("foo")
90+
ngettext("foo", "foos", 1)
91+
pgettext("context", "foo")
92+
npgettext("context", "foo", "foos", 1)
93+
dgettext("domain", "foo")
94+
dngettext("domain", "foo", "foos", 1)
95+
dpgettext("domain", "context", "foo")
96+
dnpgettext("domain", "context", "foo", "foos", 1)
97+
98+
# Complex arguments
99+
ngettext("foo", "foos", 42 + (10 - 20))
100+
dgettext(["some", {"complex"}, ("argument",)], "domain foo")
101+
102+
# Invalid calls which are not extracted
103+
gettext()
104+
ngettext('foo')
105+
pgettext('context')
106+
npgettext('context', 'foo')
107+
dgettext('domain')
108+
dngettext('domain', 'foo')
109+
dpgettext('domain', 'context')
110+
dnpgettext('domain', 'context', 'foo')

Lib/test/test_tools/test_i18n.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,14 +332,14 @@ def test_calls_in_fstring_with_multiple_args(self):
332332
msgids = self.extract_docstrings_from_str(dedent('''\
333333
f"{_('foo', 'bar')}"
334334
'''))
335-
self.assertNotIn('foo', msgids)
335+
self.assertIn('foo', msgids)
336336
self.assertNotIn('bar', msgids)
337337

338338
def test_calls_in_fstring_with_keyword_args(self):
339339
msgids = self.extract_docstrings_from_str(dedent('''\
340340
f"{_('foo', bar='baz')}"
341341
'''))
342-
self.assertNotIn('foo', msgids)
342+
self.assertIn('foo', msgids)
343343
self.assertNotIn('bar', msgids)
344344
self.assertNotIn('baz', msgids)
345345

Lib/test/translationdata/argparse/msgids.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ argument %(argument_name)s: %(message)s
88
argument '%(argument_name)s' is deprecated
99
can't open '%(filename)s': %(error)s
1010
command '%(parser_name)s' is deprecated
11+
conflicting option string: %s
12+
expected %s argument
1113
expected at least one argument
1214
expected at most one argument
1315
expected one argument

Lib/test/translationdata/optparse/msgids.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
%(option)s option requires %(number)d argument
12
%prog [options]
23
%s option does not take a value
34
Options
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix crash when calling a :func:`operator.methodcaller` instance from
2+
multiple threads in the free threading build.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Filter out memory-related ``mmap``, ``munmap``, and ``mprotect`` calls from
2+
file-related ones when testing :mod:`io` behavior using strace.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add support for multi-argument :mod:`gettext` functions in :program:`pygettext.py`.

Modules/_operator.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1602,6 +1602,7 @@ typedef struct {
16021602
vectorcallfunc vectorcall;
16031603
} methodcallerobject;
16041604

1605+
#ifndef Py_GIL_DISABLED
16051606
static int _methodcaller_initialize_vectorcall(methodcallerobject* mc)
16061607
{
16071608
PyObject* args = mc->xargs;
@@ -1664,6 +1665,7 @@ methodcaller_vectorcall(
16641665
(PyTuple_GET_SIZE(mc->xargs)) | PY_VECTORCALL_ARGUMENTS_OFFSET,
16651666
mc->vectorcall_kwnames);
16661667
}
1668+
#endif
16671669

16681670

16691671
/* AC 3.5: variable number of arguments, not currently support by AC */
@@ -1703,7 +1705,14 @@ methodcaller_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
17031705
mc->vectorcall_args = 0;
17041706

17051707

1708+
#ifdef Py_GIL_DISABLED
1709+
// gh-127065: The current implementation of methodcaller_vectorcall
1710+
// is not thread-safe because it modifies the `vectorcall_args` array,
1711+
// which is shared across calls.
1712+
mc->vectorcall = NULL;
1713+
#else
17061714
mc->vectorcall = (vectorcallfunc)methodcaller_vectorcall;
1715+
#endif
17071716

17081717
PyObject_GC_Track(mc);
17091718
return (PyObject *)mc;

0 commit comments

Comments
 (0)