Skip to content

Commit 0e37cc2

Browse files
pushinmg
2 parents 053e62f + 46cbef8 commit 0e37cc2

39 files changed

Lines changed: 830 additions & 222 deletions

.github/workflows/reusable-san.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ jobs:
3939
run: |
4040
sudo ./.github/workflows/posix-deps-apt.sh
4141
# On ubuntu-26.04 image, clang is clang-21 by default
42+
# NOTE: when bumping to a new version of clang,
43+
# please update the versions in `Tools/pixi-packages/variants.yaml` too.
4244
echo "CC=clang" >> "$GITHUB_ENV"
4345
echo "CXX=clang++" >> "$GITHUB_ENV"
4446
- name: TSan option setup

Doc/library/curses.ascii.rst

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -176,15 +176,18 @@ C library:
176176

177177
Checks for a non-ASCII character (ordinal values 0x80 and above).
178178

179-
These functions accept either integers or single-character strings; when the argument is a
180-
string, it is first converted using the built-in function :func:`ord`.
179+
These functions accept an integer, a single-character string, or a
180+
:class:`curses.complexchar`.
181+
A string is converted using the built-in function :func:`ord`, and a
182+
complexchar by the code of its single character; a complexchar that holds
183+
combining characters is not a single character and matches no class.
181184

182185
Note that all these functions check ordinal bit values derived from the
183186
character of the string you pass in; they do not actually know anything about
184187
the host machine's character encoding.
185188

186-
The following two functions take either a single-character string or integer
187-
byte value; they return a value of the same type.
189+
The following three functions take either a single-character string or an
190+
integer byte value; they return a value of the same type.
188191

189192

190193
.. function:: ascii(c)
@@ -194,16 +197,22 @@ byte value; they return a value of the same type.
194197

195198
.. function:: ctrl(c)
196199

197-
Return the control character corresponding to the given character (the character
198-
bit value is bitwise-anded with 0x1f).
200+
Return the control character corresponding to the given ASCII character (the
201+
character bit value is bitwise-anded with 0x1f). A non-ASCII character has no
202+
control character and is returned unchanged.
203+
204+
.. versionchanged:: next
205+
A non-ASCII argument is now returned unchanged instead of masked to a
206+
control character.
199207

200208

201209
.. function:: alt(c)
202210

203211
Return the 8-bit character corresponding to the given ASCII character (the
204212
character bit value is bitwise-ored with 0x80).
205213

206-
The following function takes either a single-character string or integer value;
214+
The following function takes a single-character string, an integer value, or a
215+
:class:`curses.complexchar`;
207216
it returns a string.
208217

209218

Doc/library/imaplib.rst

Lines changed: 101 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,10 @@ enclosed with either parentheses or double quotes) each string is quoted.
188188
However, the *password* argument to the ``LOGIN`` command is always quoted. If
189189
you want to avoid having an argument string quoted (eg: the *flags* argument to
190190
``STORE``) then enclose the string in parentheses (eg: ``r'(\Deleted)'``).
191-
In general, pass arguments unquoted and let the module quote them as needed.
192-
An argument that is already enclosed in double quotes is left unchanged,
193-
so that code which quotes arguments itself keeps working.
191+
Or you can quote the string yourself;
192+
an argument that is already enclosed in double quotes is left unchanged.
193+
In general, however, it is better to pass arguments unquoted
194+
and let the module quote them as needed.
194195

195196
Mailbox names are encoded as modified UTF-7 (:rfc:`3501`, section 5.1.3),
196197
so a mailbox name containing non-ASCII characters can be passed as an
@@ -211,11 +212,70 @@ or mandated results from the command. Each *data* is either a ``bytes``, or a
211212
tuple. If a tuple, then the first part is the header of the response, and the
212213
second part contains the data (ie: 'literal' value).
213214

214-
The *message_set* options to commands below is a string specifying one or more
215-
messages to be acted upon. It may be a simple message number (``'1'``), a range
216-
of message numbers (``'2:4'``), or a group of non-contiguous ranges separated by
217-
commas (``'1:3,6:9'``). A range can contain an asterisk to indicate an infinite
218-
upper bound (``'3:*'``).
215+
The *message_set* options to the commands below can be a string specifying
216+
one or more messages to be acted upon.
217+
It may be a simple message number (``'1'``),
218+
a range of message numbers (``'2:4'``),
219+
or a group of non-contiguous ranges separated by commas (``'1:3,6:9'``).
220+
A range can contain an asterisk
221+
to indicate an infinite upper bound (``'3:*'``).
222+
223+
Alternatively it can be specified using integers and :class:`range` objects.
224+
It may be a single message number or a sequence.
225+
The sequence items may be integers, ``(start, stop)`` tuples
226+
(where ``None`` or ``'*'`` stands for the last message),
227+
or :class:`range` objects.
228+
For example, ``[1, (3, 5), 8]`` and ``[range(1, 6), 8]``
229+
are both equivalent to ``'1,3:5,8'``.
230+
231+
.. versionchanged:: next
232+
Added support for the structured *message_set*.
233+
234+
Command arguments that are parenthesized lists of atoms ---
235+
such as the *flag_list* argument of :meth:`~IMAP4.store` and the *flags*
236+
argument of :meth:`~IMAP4.append`,
237+
the *names* argument of :meth:`~IMAP4.status`,
238+
the *sort_criteria* argument of :meth:`~IMAP4.sort`,
239+
or the *message_parts* argument of :meth:`~IMAP4.fetch` ---
240+
can be passed as a sequence of strings instead of a single preformatted string.
241+
For example, ``[r'\Seen', r'\Answered']``
242+
is equivalent to ``(\Seen \Answered)``.
243+
244+
.. versionchanged:: next
245+
Added support for passing these arguments as a sequence.
246+
247+
.. _imap4-params:
248+
249+
The value-bearing arguments of the search and fetch commands
250+
can be quoted by hand, but this is error prone.
251+
Instead, they may contain ``?`` placeholders that are substituted, and quoted
252+
as required, from a *params* keyword argument,
253+
in the manner of :mod:`sqlite3` parameter substitution::
254+
255+
# SEARCH FROM me@example.com SUBJECT "trip report"
256+
M.search(None, 'FROM ? SUBJECT ?', params=['me@example.com', 'trip report'])
257+
258+
# FETCH 1:5 (FLAGS BODY[HEADER.FIELDS (DATE FROM)])
259+
M.fetch('1:5', 'FLAGS BODY[HEADER.FIELDS ?]', params=[['DATE', 'FROM']])
260+
261+
The placeholders are:
262+
263+
* ``?`` --- an ``astring``: a string (which will be quoted if necessary),
264+
an integer, or a list of integers and/or strings
265+
(which will be sent as a parenthesized list);
266+
* ``?f`` --- a flag or a list of flags, sent verbatim without quoting;
267+
* ``?s`` --- a *message_set* in the structured form described above.
268+
269+
``??`` stands for a literal ``?``.
270+
271+
Substitution is only performed when *params* is given;
272+
if no *params* are given, an argument containing a literal ``?`` is unchanged.
273+
The *params* keyword is accepted by :meth:`~IMAP4.search`,
274+
:meth:`~IMAP4.fetch`, :meth:`~IMAP4.sort`, :meth:`~IMAP4.thread` and
275+
:meth:`~IMAP4.uid`.
276+
277+
.. versionadded:: next
278+
The *params* keyword argument.
219279

220280
An :class:`IMAP4` instance has the following methods:
221281

@@ -324,7 +384,7 @@ An :class:`IMAP4` instance has the following methods:
324384
Added the *message_set* and *uid* parameters.
325385

326386

327-
.. method:: IMAP4.fetch(message_set, message_parts, *, uid=False)
387+
.. method:: IMAP4.fetch(message_set, message_parts, *, uid=False, params=None)
328388

329389
Fetch (parts of) messages. *message_parts* should be a string of message part
330390
names enclosed within parentheses, eg: ``"(UID BODY[TEXT])"``. Returned data
@@ -333,8 +393,11 @@ An :class:`IMAP4` instance has the following methods:
333393
If *uid* is true, *message_set* is a set of UIDs and the message numbers in
334394
the response are UIDs (``UID FETCH``).
335395

396+
If *params* is given, ``?`` placeholders in *message_parts* are substituted
397+
with the quoted parameters (see :ref:`the placeholders <imap4-params>`).
398+
336399
.. versionchanged:: next
337-
Added the *uid* parameter.
400+
Added the *params* and *uid* parameters.
338401

339402

340403
.. method:: IMAP4.getacl(mailbox)
@@ -598,7 +661,7 @@ An :class:`IMAP4` instance has the following methods:
598661
code, instead of the usual type.
599662

600663

601-
.. method:: IMAP4.search(charset, criterion[, ...], *, uid=False)
664+
.. method:: IMAP4.search(charset, criterion[, ...], *, uid=False, params=None)
602665

603666
Search mailbox for matching messages. *charset* may be ``None``, in which case
604667
no ``CHARSET`` will be specified in the request to the server. The IMAP
@@ -617,18 +680,22 @@ An :class:`IMAP4` instance has the following methods:
617680
When *charset* is ``None`` (as it must be under ``UTF8=ACCEPT``),
618681
the criterion is sent using the connection's encoding instead.
619682

683+
If *params* is given, ``?`` placeholders in the criteria are substituted
684+
with the quoted parameters (see :ref:`the placeholders <imap4-params>`).
685+
620686
Example::
621687

622688
# M is a connected IMAP4 instance...
623-
typ, msgnums = M.search(None, 'FROM', '"LDJ"')
689+
typ, msgnums = M.search(None, 'FROM', '"John Smith"')
624690

625691
# or:
626-
typ, msgnums = M.search(None, '(FROM "LDJ")')
692+
typ, msgnums = M.search(None, '(FROM "John Smith")')
627693

628-
.. versionchanged:: next
629-
Added the *uid* parameter.
694+
# or, letting the module quote the value (this is recommended):
695+
typ, msgnums = M.search(None, 'FROM ?', params=['John Smith'])
630696

631697
.. versionchanged:: next
698+
Added the *params* and *uid* parameters.
632699
``str`` search criteria are encoded to *charset*.
633700

634701

@@ -675,7 +742,7 @@ An :class:`IMAP4` instance has the following methods:
675742
Returns socket instance used to connect to server.
676743

677744

678-
.. method:: IMAP4.sort(sort_criteria, charset, search_criterion[, ...], *, uid=False)
745+
.. method:: IMAP4.sort(sort_criteria, charset, search_criterion[, ...], *, uid=False, params=None)
679746

680747
The ``sort`` command is a variant of ``search`` with sorting semantics for the
681748
results. Returned data contains a space separated list of matching message
@@ -696,12 +763,13 @@ An :class:`IMAP4` instance has the following methods:
696763
a *search_criterion* passed as :class:`str` is encoded to *charset*;
697764
pass :class:`bytes` to send one already encoded.
698765

699-
This is an ``IMAP4rev1`` extension command.
766+
If *params* is given, ``?`` placeholders in the search criteria are
767+
substituted with the quoted parameters (see :ref:`the placeholders <imap4-params>`).
700768

701-
.. versionchanged:: next
702-
Added the *uid* parameter.
769+
This is an ``IMAP4rev1`` extension command.
703770

704771
.. versionchanged:: next
772+
Added the *params* and *uid* parameters.
705773
``str`` search criteria are encoded to *charset*.
706774

707775

@@ -745,7 +813,7 @@ An :class:`IMAP4` instance has the following methods:
745813

746814
typ, data = M.search(None, 'ALL')
747815
for num in data[0].split():
748-
M.store(num, '+FLAGS', '\\Deleted')
816+
M.store(num, '+FLAGS', r'\Deleted')
749817
M.expunge()
750818

751819
.. note::
@@ -768,7 +836,7 @@ An :class:`IMAP4` instance has the following methods:
768836
Subscribe to new mailbox.
769837

770838

771-
.. method:: IMAP4.thread(threading_algorithm, charset, search_criterion[, ...], *, uid=False)
839+
.. method:: IMAP4.thread(threading_algorithm, charset, search_criterion[, ...], *, uid=False, params=None)
772840

773841
The ``thread`` command is a variant of ``search`` with threading semantics for
774842
the results. Returned data contains a space separated list of thread members.
@@ -793,22 +861,30 @@ An :class:`IMAP4` instance has the following methods:
793861
a *search_criterion* passed as :class:`str` is encoded to *charset*;
794862
pass :class:`bytes` to send one already encoded.
795863

796-
This is an ``IMAP4rev1`` extension command.
864+
If *params* is given, ``?`` placeholders in the search criteria are
865+
substituted with the quoted parameters (see :ref:`the placeholders <imap4-params>`).
797866

798-
.. versionchanged:: next
799-
Added the *uid* parameter.
867+
This is an ``IMAP4rev1`` extension command.
800868

801869
.. versionchanged:: next
870+
Added the *params* and *uid* parameters.
802871
``str`` search criteria are encoded to *charset*.
803872

804873

805-
.. method:: IMAP4.uid(command, arg[, ...])
874+
.. method:: IMAP4.uid(command, arg[, ...], *, params=None)
806875

807876
Execute command args with messages identified by UID, rather than message
808877
number. Returns response appropriate to command. At least one argument must be
809878
supplied; if none are provided, the server will return an error and an exception
810879
will be raised.
811880

881+
If *params* is given, ``?`` placeholders in the ``SEARCH``, ``SORT`` and
882+
``THREAD`` criteria or in the ``FETCH`` parts are substituted with the quoted
883+
parameters (see :ref:`the placeholders <imap4-params>`).
884+
885+
.. versionchanged:: next
886+
Added the *params* parameter.
887+
812888

813889
.. method:: IMAP4.unsubscribe(mailbox)
814890

Doc/library/symtable.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ Generating Symbol Tables
2020
.. function:: symtable(code, filename, compile_type, *, module=None)
2121

2222
Return the toplevel :class:`SymbolTable` for the Python source *code*.
23+
*code* can be a string, a bytes object, or an AST object,
24+
as for the builtin :func:`compile`.
2325
*filename* is the name of the file containing the code. *compile_type* is
2426
like the *mode* argument to :func:`compile`.
2527
The optional argument *module* specifies the module name.
@@ -29,6 +31,9 @@ Generating Symbol Tables
2931
.. versionadded:: 3.15
3032
Added the *module* parameter.
3133

34+
.. versionchanged:: next
35+
*code* can now be an AST object.
36+
3237

3338
Examining Symbol Tables
3439
-----------------------

Doc/whatsnew/3.16.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,17 @@ imaplib
290290
the criteria are sent using the connection encoding instead.
291291
(Contributed by Serhiy Storchaka in :gh:`153494`.)
292292

293+
* Command methods now accept structured arguments,
294+
so the module takes care of quoting instead of the caller.
295+
A *message_set* and lists of flags or other atoms
296+
can be passed as sequences instead of preformatted strings,
297+
and the :meth:`~imaplib.IMAP4.search`, :meth:`~imaplib.IMAP4.fetch`,
298+
:meth:`~imaplib.IMAP4.sort`, :meth:`~imaplib.IMAP4.thread` and
299+
:meth:`~imaplib.IMAP4.uid` methods accept a *params* keyword argument
300+
that substitutes and quotes ``?`` placeholders,
301+
in the manner of :mod:`sqlite3` parameter substitution.
302+
(Contributed by Serhiy Storchaka in :gh:`153521`.)
303+
293304

294305
ipaddress
295306
---------
@@ -368,6 +379,14 @@ shlex
368379
(Contributed by Jay Berry in :gh:`148846`.)
369380

370381

382+
symtable
383+
--------
384+
385+
* :func:`symtable.symtable` now accepts an AST object,
386+
like the builtin :func:`compile`.
387+
(Contributed by Serhiy Storchaka in :gh:`153844`.)
388+
389+
371390
tkinter
372391
-------
373392

Lib/curses/ascii.py

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
"""Constants and membership tests for ASCII characters"""
22

3+
# A character-cell type, present on wide and narrow builds.
4+
from _curses import complexchar as _complexchar
5+
36
NUL = 0x00 # ^@
47
SOH = 0x01 # ^A
58
STX = 0x02 # ^B
@@ -48,8 +51,12 @@
4851
def _ctoi(c):
4952
if isinstance(c, str):
5053
return ord(c)
51-
else:
52-
return c
54+
if isinstance(c, _complexchar):
55+
# A character cell: its single character, or -1 (matches no class)
56+
# for a cell with combining characters.
57+
s = str(c)
58+
return ord(s) if len(s) == 1 else -1
59+
return c
5360

5461
def isalnum(c): return isalpha(c) or isdigit(c)
5562
def isalpha(c): return isupper(c) or islower(c)
@@ -69,22 +76,23 @@ def isctrl(c): return 0 <= _ctoi(c) < 32
6976
def ismeta(c): return _ctoi(c) > 127
7077

7178
def ascii(c):
72-
if isinstance(c, str):
73-
return chr(_ctoi(c) & 0x7f)
74-
else:
79+
if isinstance(c, int):
7580
return _ctoi(c) & 0x7f
81+
else:
82+
return chr(_ctoi(c) & 0x7f)
7683

7784
def ctrl(c):
78-
if isinstance(c, str):
79-
return chr(_ctoi(c) & 0x1f)
80-
else:
81-
return _ctoi(c) & 0x1f
85+
code = _ctoi(c)
86+
if not 0 <= code < 128:
87+
# No control character outside ASCII: return c unchanged.
88+
return c if isinstance(c, int) else str(c)
89+
return code & 0x1f if isinstance(c, int) else chr(code & 0x1f)
8290

8391
def alt(c):
84-
if isinstance(c, str):
85-
return chr(_ctoi(c) | 0x80)
86-
else:
92+
if isinstance(c, int):
8793
return _ctoi(c) | 0x80
94+
else:
95+
return chr(_ctoi(c) | 0x80)
8896

8997
def unctrl(c):
9098
bits = _ctoi(c)

0 commit comments

Comments
 (0)