Skip to content

gh-55454: Add IMAP4 IDLE support to imaplib #122542

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 80 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
572d1e5
gh-55454: Add IMAP4 IDLE support to imaplib
foresto Jul 31, 2024
dc71241
gh-55454: Clarify imaplib idle() docs
foresto Sep 11, 2024
f41f2bb
docs: words instead of <=
gpshead Sep 21, 2024
8077f2e
docs: improve style in an example
gpshead Sep 21, 2024
24fcdbb
docs: grammatical edit
gpshead Sep 21, 2024
2c76b2f
docs consistency
gpshead Sep 21, 2024
5ef5bb2
comment -> docstring
gpshead Sep 21, 2024
91266e0
docs: refer to imaplib as "this module"
foresto Sep 22, 2024
19c98dc
imaplib: simplify & clarify idle debug message
foresto Sep 22, 2024
882bf2c
imaplib: elaborate in idle context manager comment
foresto Sep 22, 2024
f648fec
imaplib: re-raise BaseException instead of bare except
foresto Sep 22, 2024
48f6f76
imaplib: convert private doc string to comment
foresto Sep 22, 2024
013bbf1
docs: correct mistake in imaplib example
foresto Sep 22, 2024
acbc4a1
imaplib: simplify example code in doc string
foresto Sep 23, 2024
94c02e8
imaplib: rename _Idler to Idler, update its docs
foresto Sep 23, 2024
0c6c9a4
imaplib: add comment in Idler._pop()
foresto Sep 23, 2024
223c2fa
imaplib: remove unnecessary blank line
foresto Sep 23, 2024
e64546c
imaplib: comment on use of unbuffered pipes
foresto Sep 23, 2024
f385e44
docs: imaplib: use the reStructuredText :class: role
foresto Sep 24, 2024
3aceaec
Revert "docs: imaplib: use the reStructuredText :class: role"
foresto Sep 24, 2024
c8d4d6d
docs: imaplib: use the reST :class: role, escaped
foresto Sep 26, 2024
75fbe8e
Merge branch 'main' into imaplib-idle
gvanrossum Dec 1, 2024
c7ed3c5
docs: refer to IMAP4 IDLE instead of just IDLE
foresto Dec 1, 2024
b01de95
imaplib: IDLE -> IMAP4 IDLE in exception message
foresto Dec 1, 2024
a3f21cd
docs: imaplib idle() phrasing and linking tweaks
foresto Dec 1, 2024
247e6b5
docs: imaplib: avoid linking to an invalid target
foresto Dec 2, 2024
79e3d83
imaplib: update test after recent exception change
foresto Dec 2, 2024
14dfd21
imaplib: rename idle() dur argument to duration
foresto Dec 2, 2024
19253d1
imaplib: bytes.index() -> bytes.find()
foresto Dec 2, 2024
b65074e
imaplib: remove no-longer-necessary statement
foresto Dec 2, 2024
5d8a40b
docs: imaplib: concise & valid method links
foresto Dec 3, 2024
fc13f75
imaplib: note data types present in IDLE responses
foresto Dec 3, 2024
3221dbc
docs: imaplib: add comma to reST changes header
foresto Dec 4, 2024
564c722
imaplib: sync doc strings with reST docs
foresto Dec 7, 2024
c9e8034
docs: imaplib: minor Idler clarifications
foresto Dec 7, 2024
9c4af2c
imaplib: idle: emit (type, [data, ...]) tuples
foresto Dec 7, 2024
59e0c6a
imaplib: while/yield instead of yield from iter()
foresto Dec 8, 2024
2e3e956
imaplib: idle: use deadline idiom when iterating
foresto Dec 9, 2024
bdde943
docs: imaplib: state duration/interval arg types
foresto Dec 9, 2024
b64c7a5
docs: imaplib: minor rephrasing of a sentence
foresto Dec 9, 2024
b73a365
docs: imaplib: reposition a paragraph
foresto Dec 9, 2024
ae74499
docs: imaplib: wrap long lines in idle() section
foresto Dec 9, 2024
92d7ce7
docs: imaplib: note: Idler objects require 'with'
foresto Dec 9, 2024
171ebf1
docs: imaplib: say that 29 minutes is 1740 seconds
foresto Dec 9, 2024
5682ef4
docs: imaplib: mark a paragraph as a 'tip'
foresto Dec 10, 2024
656e9f5
docs: imaplib: rephrase reference to MS Windows
foresto Dec 10, 2024
3b70534
imaplib: end doc string titles with a period
foresto Dec 10, 2024
60e2b6f
imaplib: idle: socket timeouts instead of select()
foresto Dec 15, 2024
def6ab5
imaplib: Idler: rename private state attributes
foresto Dec 15, 2024
8e0b6b0
imaplib: rephrase a comment in example code
foresto Dec 15, 2024
08a4536
docs: imaplib: idle: use Sphinx code-block:: pycon
foresto Dec 15, 2024
c881c8b
docs: whatsnew: imaplib: reformat IMAP4.idle entry
foresto Dec 15, 2024
266a292
imaplib: idle: make doc strings brief
foresto Dec 16, 2024
055a9bd
imaplib: Idler: split assert into two statements
foresto Dec 16, 2024
4d3f020
imaplib: Idler: move assignment out of try: block
foresto Dec 16, 2024
80aaf8d
imaplib: Idler: move __exit__() for readability
foresto Dec 16, 2024
03b5205
imaplib: Idler: move __next__() for readability
foresto Dec 16, 2024
83c8946
imaplib: test: make IdleCmdHandler a global class
foresto Dec 16, 2024
be2d2b0
docs: imaplib: idle: collapse double-spaces
foresto Dec 16, 2024
de62a3e
imaplib: warn on use of undocumented 'file' attr
foresto Dec 17, 2024
7fc8a24
imaplib: revert import reformatting
foresto Dec 17, 2024
33a1fed
imaplib: restore original exception msg formatting
foresto Dec 17, 2024
40e607a
docs: imaplib: idle: versionadded:: next
foresto Dec 17, 2024
9a07f3b
imaplib: move import statement to where it's used
foresto Dec 17, 2024
f47de53
imaplib test: RuntimeWarning on IMAP4.file access
foresto Dec 17, 2024
66d32a0
imaplib: use stacklevel=2 in warnings.warn()
foresto Dec 17, 2024
d619580
imaplib test: simplify IMAP4.file warning test
foresto Dec 17, 2024
fc8b6f4
imaplib test: pre-idle-continuation response
foresto Dec 21, 2024
b767ab6
imaplib test: post-done untagged response
foresto Dec 21, 2024
dcd0161
imaplib: downgrade idle-denied exception to error
foresto Dec 22, 2024
53c7a19
imaplib: simplify check for socket object
foresto Jan 9, 2025
fcaf355
imaplib: narrow the scope of IDLE socket timeouts
foresto Jan 9, 2025
a47bcb4
imaplib: preserve partial reads on exception
foresto Jan 10, 2025
7fc4b78
imaplib: read/readline: save multipart buffer tail
foresto Jan 10, 2025
be34141
imaplib: use TimeoutError subclass only if needed
foresto Jan 20, 2025
e8a8509
doc: imaplib: elaborate on IDLE response delivery
foresto Jan 20, 2025
8d78010
doc: imaplib: elaborate in note re: IMAP4.response
foresto Jan 20, 2025
e8f0532
Merge branch 'main' into imaplib-idle
foresto Feb 4, 2025
f650dfa
imaplib: comment on benefit of reading in chunks
foresto Feb 4, 2025
3512858
imaplib: readline(): treat ConnectionError as EOF
foresto Feb 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 89 additions & 1 deletion Doc/library/imaplib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
.. changes for IMAP4_SSL by Tino Lange <[email protected]>, March 2002
.. changes for IMAP4_stream by Piers Lauder <[email protected]>,
November 2002
.. changes for IMAP4 IDLE by Forest <[email protected]>, August 2024

**Source code:** :source:`Lib/imaplib.py`

Expand Down Expand Up @@ -187,7 +188,7 @@ However, the *password* argument to the ``LOGIN`` command is always quoted. If
you want to avoid having an argument string quoted (eg: the *flags* argument to
``STORE``) then enclose the string in parentheses (eg: ``r'(\Deleted)'``).

Each command returns a tuple: ``(type, [data, ...])`` where *type* is usually
Most commands return a tuple: ``(type, [data, ...])`` where *type* is usually
``'OK'`` or ``'NO'``, and *data* is either the text from the command response,
or mandated results from the command. Each *data* is either a ``bytes``, or a
tuple. If a tuple, then the first part is the header of the response, and the
Expand Down Expand Up @@ -307,6 +308,93 @@ An :class:`IMAP4` instance has the following methods:
of the IMAP4 QUOTA extension defined in rfc2087.


.. method:: IMAP4.idle(duration=None)

Return an :class:`!Idler`: an iterable context manager implementing the
IMAP4 ``IDLE`` command as defined in :rfc:`2177`.

The returned object sends the ``IDLE`` command when activated by the
:keyword:`with` statement, produces IMAP untagged responses via the
:term:`iterator` protocol, and sends ``DONE`` upon context exit.

All untagged responses that arrive after sending the ``IDLE`` command
(including any that arrive before the server acknowledges the command) will
be available via iteration. Any leftover responses (those not iterated in
the :keyword:`with` context) can be retrieved in the usual way after
``IDLE`` ends, using :meth:`IMAP4.response`.

Responses are represented as ``(type, [data, ...])`` tuples, as described
in :ref:`IMAP4 Objects <imap4-objects>`.

The *duration* argument sets a maximum duration (in seconds) to keep idling,
after which any ongoing iteration will stop. It can be an :class:`int` or
:class:`float`, or ``None`` for no time limit.
Callers wishing to avoid inactivity timeouts on servers that impose them
should keep this at most 29 minutes (1740 seconds).
Requires a socket connection; *duration* must be ``None`` on
:class:`IMAP4_stream` connections.

.. code-block:: pycon

>>> with M.idle(duration=29 * 60) as idler:
... for typ, data in idler:
... print(typ, data)
...
EXISTS [b'1']
RECENT [b'1']


.. method:: Idler.burst(interval=0.1)

Yield a burst of responses no more than *interval* seconds apart
(expressed as an :class:`int` or :class:`float`).

This :term:`generator` is an alternative to iterating one response at a
time, intended to aid in efficient batch processing. It retrieves the
next response along with any immediately available subsequent responses.
(For example, a rapid series of ``EXPUNGE`` responses after a bulk
delete.)

Requires a socket connection; does not work on :class:`IMAP4_stream`
connections.

.. code-block:: pycon

>>> with M.idle() as idler:
... # get a response and any others following by < 0.1 seconds
... batch = list(idler.burst())
... print(f'processing {len(batch)} responses...')
... print(batch)
...
processing 3 responses...
[('EXPUNGE', [b'2']), ('EXPUNGE', [b'1']), ('RECENT', [b'0'])]

.. tip::

The ``IDLE`` context's maximum duration, as passed to
:meth:`IMAP4.idle`, is respected when waiting for the first response
in a burst. Therefore, an expired :class:`!Idler` will cause this
generator to return immediately without producing anything. Callers
should consider this if using it in a loop.


.. note::

The iterator returned by :meth:`IMAP4.idle` is usable only within a
:keyword:`with` statement. Before or after that context, unsolicited
responses are collected internally whenever a command finishes, and can
be retrieved with :meth:`IMAP4.response`.

.. note::

The :class:`!Idler` class name and structure are internal interfaces,
subject to change. Calling code can rely on its context management,
iteration, and public method to remain stable, but should not subclass,
instantiate, compare, or otherwise directly reference the class.

.. versionadded:: next


.. method:: IMAP4.list([directory[, pattern]])

List mailbox names in *directory* matching *pattern*. *directory* defaults to
Expand Down
6 changes: 6 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,12 @@ inspect
:term:`package` or not.
(Contributed by Zhikang Yan in :gh:`125634`.)

imaplib
-------

* Add :meth:`IMAP4.idle() <imaplib.IMAP4.idle>`, implementing the IMAP4
``IDLE`` command as defined in :rfc:`2177`.
(Contributed by Forest in :gh:`55454`.)


io
Expand Down
Loading
Loading