Skip to content

Commit 620a344

Browse files
authored
Merge branch 'main' into fix-exeptions-append-after-cancel-async-stagger
2 parents 04532ab + a62ba52 commit 620a344

File tree

11 files changed

+80
-16
lines changed

11 files changed

+80
-16
lines changed

.github/workflows/reusable-docs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ jobs:
9999
# Run "doctest" on HEAD as new syntax doesn't exist in the latest stable release
100100
doctest:
101101
name: 'Doctest'
102-
runs-on: ubuntu-22.04
102+
runs-on: ubuntu-24.04
103103
timeout-minutes: 60
104104
steps:
105105
- uses: actions/checkout@v4

Doc/library/email.policy.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ added matters. To illustrate::
267267

268268
Handle a *defect* found on *obj*. When the email package calls this
269269
method, *defect* will always be a subclass of
270-
:class:`~email.errors.Defect`.
270+
:class:`~email.errors.MessageDefect`.
271271

272272
The default implementation checks the :attr:`raise_on_defect` flag. If
273273
it is ``True``, *defect* is raised as an exception. If it is ``False``
@@ -277,7 +277,7 @@ added matters. To illustrate::
277277
.. method:: register_defect(obj, defect)
278278

279279
Register a *defect* on *obj*. In the email package, *defect* will always
280-
be a subclass of :class:`~email.errors.Defect`.
280+
be a subclass of :class:`~email.errors.MessageDefect`.
281281

282282
The default implementation calls the ``append`` method of the ``defects``
283283
attribute of *obj*. When the email package calls :attr:`handle_defect`,

Doc/tools/.nitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ Doc/library/email.charset.rst
2323
Doc/library/email.compat32-message.rst
2424
Doc/library/email.errors.rst
2525
Doc/library/email.parser.rst
26-
Doc/library/email.policy.rst
2726
Doc/library/exceptions.rst
2827
Doc/library/functools.rst
2928
Doc/library/http.cookiejar.rst

Include/cpython/unicodeobject.h

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ typedef struct {
109109
3: Interned, Immortal, and Static
110110
This categorization allows the runtime to determine the right
111111
cleanup mechanism at runtime shutdown. */
112-
unsigned int interned:2;
112+
uint16_t interned;
113113
/* Character size:
114114
115115
- PyUnicode_1BYTE_KIND (1):
@@ -132,21 +132,23 @@ typedef struct {
132132
* all characters are in the range U+0000-U+10FFFF
133133
* at least one character is in the range U+10000-U+10FFFF
134134
*/
135-
unsigned int kind:3;
135+
unsigned short kind:3;
136136
/* Compact is with respect to the allocation scheme. Compact unicode
137137
objects only require one memory block while non-compact objects use
138138
one block for the PyUnicodeObject struct and another for its data
139139
buffer. */
140-
unsigned int compact:1;
140+
unsigned short compact:1;
141141
/* The string only contains characters in the range U+0000-U+007F (ASCII)
142142
and the kind is PyUnicode_1BYTE_KIND. If ascii is set and compact is
143143
set, use the PyASCIIObject structure. */
144-
unsigned int ascii:1;
144+
unsigned short ascii:1;
145145
/* The object is statically allocated. */
146-
unsigned int statically_allocated:1;
146+
unsigned short statically_allocated:1;
147147
/* Padding to ensure that PyUnicode_DATA() is always aligned to
148-
4 bytes (see issue #19537 on m68k). */
149-
unsigned int :24;
148+
4 bytes (see issue #19537 on m68k) and we use unsigned short to avoid
149+
the extra four bytes on 32-bit Windows. This is restricted features
150+
for specific compilers including GCC, MSVC, Clang and IBM's XL compiler. */
151+
unsigned short :10;
150152
} state;
151153
} PyASCIIObject;
152154

@@ -195,7 +197,11 @@ typedef struct {
195197

196198
/* Use only if you know it's a string */
197199
static inline unsigned int PyUnicode_CHECK_INTERNED(PyObject *op) {
200+
#ifdef Py_GIL_DISABLED
201+
return _Py_atomic_load_uint16_relaxed(&_PyASCIIObject_CAST(op)->state.interned);
202+
#else
198203
return _PyASCIIObject_CAST(op)->state.interned;
204+
#endif
199205
}
200206
#define PyUnicode_CHECK_INTERNED(op) PyUnicode_CHECK_INTERNED(_PyObject_CAST(op))
201207

Lib/email/message.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,12 @@ def get_payload(self, i=None, decode=False):
286286
if i is not None and not isinstance(self._payload, list):
287287
raise TypeError('Expected list, got %s' % type(self._payload))
288288
payload = self._payload
289-
# cte might be a Header, so for now stringify it.
290-
cte = str(self.get('content-transfer-encoding', '')).lower()
289+
cte = self.get('content-transfer-encoding', '')
290+
if hasattr(cte, 'cte'):
291+
cte = cte.cte
292+
else:
293+
# cte might be a Header, so for now stringify it.
294+
cte = str(cte).strip().lower()
291295
# payload may be bytes here.
292296
if not decode:
293297
if isinstance(payload, str) and utils._has_surrogates(payload):

Lib/test/test_email/test_email.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,16 @@ def test_unicode_body_defaults_to_utf8_encoding(self):
810810
w4kgdGVzdGFiYwo=
811811
"""))
812812

813+
def test_string_payload_with_base64_cte(self):
814+
msg = email.message_from_string(textwrap.dedent("""\
815+
Content-Transfer-Encoding: base64
816+
817+
SGVsbG8uIFRlc3Rpbmc=
818+
"""), policy=email.policy.default)
819+
self.assertEqual(msg.get_payload(decode=True), b"Hello. Testing")
820+
self.assertDefectsEqual(msg['content-transfer-encoding'].defects, [])
821+
822+
813823

814824
# Test the email.encoders module
815825
class TestEncoders(unittest.TestCase):
@@ -2352,6 +2362,40 @@ def test_missing_header_body_separator(self):
23522362
self.assertDefectsEqual(msg.defects,
23532363
[errors.MissingHeaderBodySeparatorDefect])
23542364

2365+
def test_string_payload_with_extra_space_after_cte(self):
2366+
# https://github.com/python/cpython/issues/98188
2367+
cte = "base64 "
2368+
msg = email.message_from_string(textwrap.dedent(f"""\
2369+
Content-Transfer-Encoding: {cte}
2370+
2371+
SGVsbG8uIFRlc3Rpbmc=
2372+
"""), policy=email.policy.default)
2373+
self.assertEqual(msg.get_payload(decode=True), b"Hello. Testing")
2374+
self.assertDefectsEqual(msg['content-transfer-encoding'].defects, [])
2375+
2376+
def test_string_payload_with_extra_text_after_cte(self):
2377+
msg = email.message_from_string(textwrap.dedent("""\
2378+
Content-Transfer-Encoding: base64 some text
2379+
2380+
SGVsbG8uIFRlc3Rpbmc=
2381+
"""), policy=email.policy.default)
2382+
self.assertEqual(msg.get_payload(decode=True), b"Hello. Testing")
2383+
cte = msg['content-transfer-encoding']
2384+
self.assertDefectsEqual(cte.defects, [email.errors.InvalidHeaderDefect])
2385+
2386+
def test_string_payload_with_extra_space_after_cte_compat32(self):
2387+
cte = "base64 "
2388+
msg = email.message_from_string(textwrap.dedent(f"""\
2389+
Content-Transfer-Encoding: {cte}
2390+
2391+
SGVsbG8uIFRlc3Rpbmc=
2392+
"""), policy=email.policy.compat32)
2393+
pasted_cte = msg['content-transfer-encoding']
2394+
self.assertEqual(pasted_cte, cte)
2395+
self.assertEqual(msg.get_payload(decode=True), b"Hello. Testing")
2396+
self.assertDefectsEqual(msg.defects, [])
2397+
2398+
23552399

23562400
# Test RFC 2047 header encoding and decoding
23572401
class TestRFC2047(TestEmailBase):

Lib/test/test_email/test_headerregistry.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,11 @@ def cte_as_value(self,
837837
'7bit',
838838
[errors.InvalidHeaderDefect]),
839839

840+
'extra_space_after_cte': (
841+
'base64 ',
842+
'base64',
843+
[]),
844+
840845
}
841846

842847

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,6 +1129,7 @@ Gregor Lingl
11291129
Everett Lipman
11301130
Mirko Liss
11311131
Alexander Liu
1132+
Hui Liu
11321133
Yuan Liu
11331134
Nick Lockwood
11341135
Stephanie Lockwood
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Update :c:type:`PyASCIIObject` layout to handle interned field with the
2+
atomic operation. Patch by Donghee Na.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix an issue in :meth:`email.message.Message.get_payload` where data
2+
cannot be decoded if the Content Transfer Encoding mechanism contains
3+
trailing whitespaces or additional junk text. Patch by Hui Liu.

0 commit comments

Comments
 (0)