Skip to content

Commit 30cf6c7

Browse files
committed
Merge branch 'main' into windows-add-sysconfig-abiflags
2 parents b667dd9 + c432d01 commit 30cf6c7

File tree

21 files changed

+243
-65
lines changed

21 files changed

+243
-65
lines changed

Doc/library/socket.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -882,10 +882,10 @@ The following functions all create :ref:`socket objects <socket-objects>`.
882882
, a default reasonable value is chosen.
883883
*reuse_port* dictates whether to set the :data:`SO_REUSEPORT` socket option.
884884

885-
If *dualstack_ipv6* is true and the platform supports it the socket will
886-
be able to accept both IPv4 and IPv6 connections, else it will raise
887-
:exc:`ValueError`. Most POSIX platforms and Windows are supposed to support
888-
this functionality.
885+
If *dualstack_ipv6* is true, *family* is :data:`AF_INET6` and the platform
886+
supports it the socket will be able to accept both IPv4 and IPv6 connections,
887+
else it will raise :exc:`ValueError`. Most POSIX platforms and Windows are
888+
supposed to support this functionality.
889889
When this functionality is enabled the address returned by
890890
:meth:`socket.getpeername` when an IPv4 connection occurs will be an IPv6
891891
address represented as an IPv4-mapped IPv6 address.

Doc/library/stdtypes.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4780,7 +4780,7 @@ can be used interchangeably to index the same dictionary entry.
47804780
such as an empty list. To get distinct values, use a :ref:`dict
47814781
comprehension <dict>` instead.
47824782

4783-
.. method:: get(key, default=None)
4783+
.. method:: get(key, default=None, /)
47844784

47854785
Return the value for *key* if *key* is in the dictionary, else *default*.
47864786
If *default* is not given, it defaults to ``None``, so that this method
@@ -4822,7 +4822,7 @@ can be used interchangeably to index the same dictionary entry.
48224822

48234823
.. versionadded:: 3.8
48244824

4825-
.. method:: setdefault(key, default=None)
4825+
.. method:: setdefault(key, default=None, /)
48264826

48274827
If *key* is in the dictionary, return its value. If not, insert *key*
48284828
with a value of *default* and return *default*. *default* defaults to

Include/internal/pycore_symtable.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ typedef struct _symtable_entry {
126126
unsigned ste_method : 1; /* true if block is a function block defined in class scope */
127127
unsigned ste_has_conditional_annotations : 1; /* true if block has conditionally executed annotations */
128128
unsigned ste_in_conditional_block : 1; /* set while we are inside a conditionally executed block */
129+
unsigned ste_in_unevaluated_annotation : 1; /* set while we are processing an annotation that will not be evaluated */
129130
int ste_comp_iter_expr; /* non-zero if visiting a comprehension range expression */
130131
_Py_SourceLocation ste_loc; /* source location of block */
131132
struct _symtable_entry *ste_annotation_block; /* symbol table entry for this entry's annotations */

Lib/email/_policybase.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"""
55

66
import abc
7+
import re
78
from email import header
89
from email import charset as _charset
910
from email.utils import _has_surrogates
@@ -14,6 +15,14 @@
1415
'compat32',
1516
]
1617

18+
# validation regex from RFC 5322, equivalent to pattern re.compile("[!-9;-~]+$")
19+
valid_header_name_re = re.compile("[\041-\071\073-\176]+$")
20+
21+
def validate_header_name(name):
22+
# Validate header name according to RFC 5322
23+
if not valid_header_name_re.match(name):
24+
raise ValueError(
25+
f"Header field name contains invalid characters: {name!r}")
1726

1827
class _PolicyBase:
1928

@@ -314,6 +323,7 @@ def header_store_parse(self, name, value):
314323
"""+
315324
The name and value are returned unmodified.
316325
"""
326+
validate_header_name(name)
317327
return (name, value)
318328

319329
def header_fetch_parse(self, name, value):

Lib/email/policy.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@
44

55
import re
66
import sys
7-
from email._policybase import Policy, Compat32, compat32, _extend_docstrings
7+
from email._policybase import (
8+
Compat32,
9+
Policy,
10+
_extend_docstrings,
11+
compat32,
12+
validate_header_name
13+
)
814
from email.utils import _has_surrogates
915
from email.headerregistry import HeaderRegistry as HeaderRegistry
1016
from email.contentmanager import raw_data_manager
@@ -138,6 +144,7 @@ def header_store_parse(self, name, value):
138144
CR or LF characters.
139145
140146
"""
147+
validate_header_name(name)
141148
if hasattr(value, 'name') and value.name.lower() == name.lower():
142149
return (name, value)
143150
if isinstance(value, str) and len(value.splitlines())>1:

Lib/test/test_ctypes/test_dlerror.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def test_null_dlsym(self):
120120
# Assert that the IFUNC was called
121121
self.assertEqual(os.read(pipe_r, 2), b'OK')
122122

123-
123+
@test.support.thread_unsafe('setlocale is not thread-safe')
124124
@unittest.skipUnless(os.name != 'nt', 'test requires dlerror() calls')
125125
class TestLocalization(unittest.TestCase):
126126

Lib/test/test_email/test_email.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,31 @@ def test_nonascii_add_header_with_tspecial(self):
728728
"attachment; filename*=utf-8''Fu%C3%9Fballer%20%5Bfilename%5D.ppt",
729729
msg['Content-Disposition'])
730730

731+
def test_invalid_header_names(self):
732+
invalid_headers = [
733+
('Invalid Header', 'contains space'),
734+
('Tab\tHeader', 'contains tab'),
735+
('Colon:Header', 'contains colon'),
736+
('', 'Empty name'),
737+
(' LeadingSpace', 'starts with space'),
738+
('TrailingSpace ', 'ends with space'),
739+
('Header\x7F', 'Non-ASCII character'),
740+
('Header\x80', 'Extended ASCII'),
741+
]
742+
for policy in (email.policy.default, email.policy.compat32):
743+
for setter in (Message.__setitem__, Message.add_header):
744+
for name, value in invalid_headers:
745+
self.do_test_invalid_header_names(
746+
policy, setter,name, value)
747+
748+
def do_test_invalid_header_names(self, policy, setter, name, value):
749+
with self.subTest(policy=policy, setter=setter, name=name, value=value):
750+
message = Message(policy=policy)
751+
pattern = r'(?i)(?=.*invalid)(?=.*header)(?=.*name)'
752+
with self.assertRaisesRegex(ValueError, pattern) as cm:
753+
setter(message, name, value)
754+
self.assertIn(f"{name!r}", str(cm.exception))
755+
731756
def test_binary_quopri_payload(self):
732757
for charset in ('latin-1', 'ascii'):
733758
msg = Message()

Lib/test/test_email/test_message.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,30 @@ def test_folding_with_long_nospace_http_policy_1(self):
10041004
parsed_msg = message_from_bytes(m.as_bytes(), policy=policy.default)
10051005
self.assertEqual(parsed_msg['Message-ID'], m['Message-ID'])
10061006

1007+
def test_invalid_header_names(self):
1008+
invalid_headers = [
1009+
('Invalid Header', 'contains space'),
1010+
('Tab\tHeader', 'contains tab'),
1011+
('Colon:Header', 'contains colon'),
1012+
('', 'Empty name'),
1013+
(' LeadingSpace', 'starts with space'),
1014+
('TrailingSpace ', 'ends with space'),
1015+
('Header\x7F', 'Non-ASCII character'),
1016+
('Header\x80', 'Extended ASCII'),
1017+
]
1018+
for email_policy in (policy.default, policy.compat32):
1019+
for setter in (EmailMessage.__setitem__, EmailMessage.add_header):
1020+
for name, value in invalid_headers:
1021+
self.do_test_invalid_header_names(email_policy, setter, name, value)
1022+
1023+
def do_test_invalid_header_names(self, policy, setter, name, value):
1024+
with self.subTest(policy=policy, setter=setter, name=name, value=value):
1025+
message = EmailMessage(policy=policy)
1026+
pattern = r'(?i)(?=.*invalid)(?=.*header)(?=.*name)'
1027+
with self.assertRaisesRegex(ValueError, pattern) as cm:
1028+
setter(message, name, value)
1029+
self.assertIn(f"{name!r}", str(cm.exception))
1030+
10071031
def test_get_body_malformed(self):
10081032
"""test for bpo-42892"""
10091033
msg = textwrap.dedent("""\

Lib/test/test_timeit.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -297,9 +297,7 @@ def test_main_negative_reps(self):
297297
@unittest.skipIf(sys.flags.optimize >= 2, "need __doc__")
298298
def test_main_help(self):
299299
s = self.run_main(switches=['-h'])
300-
# Note: It's not clear that the trailing space was intended as part of
301-
# the help text, but since it's there, check for it.
302-
self.assertEqual(s, timeit.__doc__ + ' ')
300+
self.assertEqual(s, timeit.__doc__)
303301

304302
def test_main_verbose(self):
305303
s = self.run_main(switches=['-v'])

Lib/test/test_tools/test_msgfmt.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,11 @@ def test_compilation(self):
4242
self.assertDictEqual(actual._catalog, expected._catalog)
4343

4444
def test_binary_header(self):
45-
with open(data_dir / "general.mo", "rb") as f:
46-
mo_data = f.read()
45+
with temp_cwd():
46+
tmp_mo_file = 'messages.mo'
47+
compile_messages(data_dir / "general.po", tmp_mo_file)
48+
with open(tmp_mo_file, 'rb') as f:
49+
mo_data = f.read()
4750

4851
(
4952
magic,
@@ -53,7 +56,7 @@ def test_binary_header(self):
5356
trans_table_offset,
5457
hash_table_size,
5558
hash_table_offset,
56-
) = struct.unpack("=Iiiiiii", mo_data[:28])
59+
) = struct.unpack("=7I", mo_data[:28])
5760

5861
self.assertEqual(magic, 0x950412de)
5962
self.assertEqual(version, 0)

0 commit comments

Comments
 (0)