@@ -188,9 +188,10 @@ enclosed with either parentheses or double quotes) each string is quoted.
188188However, the *password * argument to the ``LOGIN `` command is always quoted. If
189189you 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
195196Mailbox names are encoded as modified UTF-7 (:rfc: `3501 `, section 5.1.3),
196197so 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
211212tuple. If a tuple, then the first part is the header of the response, and the
212213second 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
220280An :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
0 commit comments