{"id":19361590,"url":"https://github.com/mxk/py-imaplib2","last_synced_at":"2026-05-17T19:01:19.147Z","repository":{"id":34956321,"uuid":"39031616","full_name":"mxk/py-imaplib2","owner":"mxk","description":"New imaplib implementation for Python 3.2+ standard library","archived":false,"fork":false,"pushed_at":"2015-07-13T19:06:34.000Z","size":160,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-13T03:49:40.560Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mxk.png","metadata":{"files":{"readme":"README","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-07-13T19:04:29.000Z","updated_at":"2015-07-13T19:06:34.000Z","dependencies_parsed_at":"2022-09-06T01:00:51.388Z","dependency_job_id":null,"html_url":"https://github.com/mxk/py-imaplib2","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mxk/py-imaplib2","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mxk%2Fpy-imaplib2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mxk%2Fpy-imaplib2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mxk%2Fpy-imaplib2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mxk%2Fpy-imaplib2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mxk","download_url":"https://codeload.github.com/mxk/py-imaplib2/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mxk%2Fpy-imaplib2/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33151625,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-17T09:28:26.183Z","status":"ssl_error","status_checked_at":"2026-05-17T09:27:52.702Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-10T07:24:01.672Z","updated_at":"2026-05-17T19:01:19.108Z","avatar_url":"https://github.com/mxk.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\r\n\r\n\r\n                     New IMAP4rev1 library for Python 3.2+\r\n                        Maxim Khitrov \u003cmax@mxcrypt.com\u003e\r\n                                  24 July 2011\r\n\r\n\r\n\r\nABSTRACT\r\n========\r\n\r\nThe imaplib2 library provides a complete implementation of the IMAP4rev1\r\nprotocol described in [RFC-3501], as well as over a dozen additional extensions.\r\nThe implementation supports all existing features of imaplib and makes several\r\nimprovements, which simplify the task of implementing an IMAP4 client\r\napplication and provide access to more advanced protocol features.\r\n\r\n\r\nRATIONALE\r\n=========\r\n\r\nThe existing imaplib module implements a subset of the older [RFC-2060]\r\nprotocol, and opts for simplicity in handling server responses by delegating\r\nmost of the parsing work to the caller. This makes writing an IMAP4 client\r\napplication more difficult, because the developer must first understand the\r\nnuances of what servers are allowed to send and when.\r\n\r\nThe protocol explicitly allows servers to send unrequested (unilateral) data to\r\nthe client at any time. Servers may inject additional information into a\r\nresponse, such as an unrequested FLAGS attribute for a FETCH command, which may\r\nhappen if the mailbox is opened by multiple clients. The data for a single\r\nmessage may also be split into several responses at the server's discretion. All\r\nof these permutations have the potential to cause problems for application code\r\nwritten to expect a particular form of a response in order to parse it using\r\nregular expressions.\r\n\r\nFor more advanced use cases, the features missing from the existing library\r\nimpact the overall application performance by preventing client applications\r\nfrom issuing multiple commands or processing server responses in an asynchronous\r\nfashion. These features cannot be easily added to imaplib, because they go\r\nagainst the existing command interface.\r\n\r\nThe new library attempts to correct these issues by providing a redesigned\r\ninterface for sending commands to the server and processing responses. It also\r\ncontains several utility functions for encoding and decoding various types of\r\ndata. The implementation closely follows [RFC-3501], which obsoletes [RFC-2060].\r\nA list of differences between the two is available at:\r\n\r\n* http://tools.ietf.org/html/rfc3501#appendix-B\r\n\r\nThe following is an outline of the major additions and improvements made in the\r\nnew library:\r\n\r\n* Server response parser. The parser implements proper handling for all IMAP4\r\n  data types, including quoted strings, literals, nested parenthesized lists,\r\n  numbers, atoms, and NILs. The final output is presented to the user as a list\r\n  of decoded tokens with several attributes to help identify the response type\r\n  and destination (see the next section for examples).\r\n\r\n* Concurrent command execution. With the exception of a few commands (e.g.\r\n  STARTTLS), the protocol allows the client to issue multiple commands at the\r\n  same time whenever there is no ambiguity in expected responses. This is not\r\n  possible in imaplib, but could be useful for improving performance or\r\n  interactivity in some client applications.\r\n\r\n* Data compression. The COMPRESS extension [RFC-4978] is fully supported, which\r\n  allows the incoming and outgoing data streams to be compressed using zlib's\r\n  DEFLATE algorithm. This has a huge impact on the total amount of data\r\n  transferred in a single session (e.g. a complete download of a 3 GB mailbox\r\n  results in a transfer of just 986 MB).\r\n\r\n* Access to received data while a command is in progress (aka asynchronous\r\n  execution). Some commands, such as FETCH, may run for several minutes while\r\n  the server locates the requested data and sends it to the client. In these\r\n  instances, there is a significant performance benefit in processing the\r\n  responses as they are coming in, as opposed to buffering everything in memory.\r\n  Some commands, such as IDLE [RFC-2177], can only operate asynchronously, which\r\n  requires library support for response polling.\r\n\r\n* UTF-7 mailbox name codec. IMAP4 uses a modified UTF-7 encoding for mailbox\r\n  names containing non-ASCII characters. Python has no existing method of\r\n  decoding this format, which is different from [RFC-2152] and 'utf_7' codec.\r\n  Two new functions have been added to encode and decode this format, and the\r\n  encoder is called automatically by the library for all commands expecting a\r\n  mailbox name as input.\r\n\r\n* Support for multiple literals in commands. [RFC-3501] section 7.5 shows an\r\n  example of a LOGIN command containing two literals, which could be necessary\r\n  when username and password use 8-bit characters. Other commands may also\r\n  contain multiple literals, which is not supported by imaplib. The new library\r\n  correctly handles any number of literals in the command by treating strings as\r\n  ASCII text and bytes as literal data. Commands like LOGIN automatically\r\n  convert their arguments into the literal form if they contain non-ASCII\r\n  characters. The library also supports LITERAL+ extension [RFC-2088], which\r\n  permits the client to begin sending the next literal without waiting for a go\r\n  ahead from the server.\r\n\r\n* Many additional extensions have been implemented to support the features\r\n  available in modern IMAP4 servers. The full list of RFCs is at the top of\r\n  imaplib2.py.\r\n\r\n\r\nIMPLEMENTATION\r\n==============\r\n\r\nThis section describes the core classes of the imaplib2 module:\r\n\r\n* IMAP4\r\n* IMAP4Command\r\n* IMAP4Response\r\n* IMAP4SeqSet\r\n\r\nIMAP4\r\n-----\r\n\r\nA connection to the server is established by creating a new IMAP4 class\r\ninstance. The resulting object supports the context management protocol, which\r\nautomatically performs a graceful logout upon leaving the context:\r\n\r\n    from imaplib2 import *\r\n\r\n    with IMAP4('host:port', timeout=30) as imap:\r\n        ...\r\n\r\n    # ^ Can also be written as:\r\n    imap = IMAP4(('host', port), timeout=30)\r\n    try:\r\n        ...\r\n    finally:\r\n        imap.logout()\r\n\r\nThe library takes care of most protocol details, such as maintaining the current\r\nconnection state (available via the 'imap.state' property), automatically\r\nupdating server capabilities when necessary, and monitoring various error\r\nconditions, such as an unexpected BYE response (server closing the connection).\r\nThis mirrors the behavior of imaplib. The interface for issuing commands is also\r\nvery similar or even identical, depending on the command:\r\n\r\n    imap.login('username', 'password')\r\n    imap.select('INBOX', readonly=True)\r\n    ...\r\n\r\nSSL/TLS encryption with optional certificate verification is enabled by\r\nproviding an ssl.SSLContext object either to the IMAP4 constructor or the\r\nSTARTTLS command:\r\n\r\n    from imaplib2 import *\r\n\r\n    # Raise SSLError if certificate verification fails\r\n    ctx = ssl_context(ssl.CERT_REQUIRED, '/path/to/cafile')\r\n\r\n    # Use SSL (default port 993 is used)\r\n    with IMAP4('imap.example.com', ssl_ctx=ctx) as imap:\r\n        imap.login('username', 'my plaintext password')  # Encrypted\r\n        ...\r\n\r\n    # Use TLS (default port 143 is used)\r\n    with IMAP4('imap.example.com') as imap:\r\n        imap.noop()                                      # Not encrypted\r\n        imap.starttls(ctx)\r\n        imap.login('username', 'my plaintext password')  # Encrypted\r\n        ...\r\n\r\nThe differences between the two libraries become more apparent after a command\r\nis issued. First, the library maintains stricter control over what the client is\r\npermitted to send at any given time. For example, the LOGIN command will not be\r\nsent if the server is advertising LOGINDISABLED capability, as required by\r\n[RFC-3501]. This is an important security consideration. Second, most command\r\nmethods gained a new 'wait' keyword argument, which allows the user to execute\r\ncommands asynchronously. This flag is set to True by default, which blocks the\r\ncaller until the entire command is finished (same as imaplib). By specifying\r\n'wait=False', control is returned to the caller as soon as the command is sent\r\nto the server. For example:\r\n\r\n    # This loop will wait for all message bodies to be buffered in memory before\r\n    # the first iteration takes place.\r\n    for resp in imap.fetch('1:*', 'BODY[]'):\r\n        ...\r\n\r\n    # This loop will run as soon as the first message body is received.\r\n    for resp in imap.fetch('1:*', 'BODY[]', wait=False):\r\n        ...\r\n\r\nThe first mode of operation is the only one implemented by imaplib. Running this\r\nFETCH command for all messages in the mailbox ('1:*') is probably a bad idea,\r\nbecause the library may end up buffering hundreds of megs in memory before\r\nprocessing anything.\r\n\r\nThe asynchronous approach is an improvement, because it provides access to each\r\nserver response as soon as it is received. The application is able to process\r\none response at a time, allowing the allocated memory to be freed by the time\r\nthe next response is received. This improves the overall performance by\r\ninterleaving I/O- and CPU-bound tasks (especially if response processing can be\r\ndelegated to another thread or process) and keeps memory usage under control.\r\n\r\nIMAP4Command\r\n------------\r\n\r\nA minor code adjustment is necessary when running commands asynchronously. In\r\nsynchronous mode, imaplib2 will raise an exception whenever the server returns\r\nNO or BAD command completion codes. This is true for all commands. In\r\nasynchronous mode, the completion code is unknown while responses are being\r\nprocessed, so the user is expected to make an extra call to the 'check()' method\r\nat the end of the loop to verify successful command completion. This also allows\r\nthe user to decide which completion codes should be treated as errors. The\r\nasynchronous example above is modified as follows:\r\n\r\n    # This loop will run as soon as the first message body is received.\r\n    cmd = imap.fetch('1:*', 'BODY[]', wait=False)\r\n    for resp in cmd:\r\n        ...\r\n    cmd.check()  # or cmd.check('OK'[, 'NO'[, 'BAD']])\r\n\r\nCalling 'cmd.check()' or 'cmd.check(\"OK\")' will raise an exception if the\r\ncommand completed with a status code other than OK. Sometimes, NO may also be an\r\nacceptable completion result, such as when a long-running FETCH fails to locate\r\nthe data or attributes of one or two messages. This is usually a temporary error\r\ncondition, which does not invalidate the data or attributes returned for all\r\nother FETCHed messages. In this case, the user would call\r\n'cmd.check(\"OK\", \"NO\")' to indicate that either response code is acceptable (BAD\r\nwill still raise an exception).\r\n\r\nThe last example made explicit the fact that executing any command causes an\r\nIMAP4Command instance to be returned. This is a major departure from imaplib,\r\nwhich would return a tuple '(type, [data, ...])'. The IMAP4Command class\r\nprovides all the tools necessary for controlling command execution, receiving\r\nand filtering responses, and determining the final command result. It is also\r\nthe base class used for implementing custom commands outside of the library.\r\n\r\nServer responses are read from the socket by calling IMAP4.__next__ method. This\r\nmeans that the user is able to monitor all responses in the order they were sent\r\nsimply by iterating over the original 'imap' object:\r\n\r\n    imap.fetch('1:*', 'BODY[]', wait=False)\r\n    for resp in imap:\r\n        ...  # resp could be a FETCH response or anything else the server sends\r\n\r\nReceiving data in this fashion is possible, but could be error-prone, because\r\nthe IMAP4 protocol explicitly permits the server to send unilateral responses\r\nthat do not belong to any command in progress (e.g. informing the client of a\r\nnew message delivered to the current mailbox). The IMAP4Command class provides a\r\nway to retrieve only those responses that are expected. To achieve this, each\r\ncommand instance has a 'queue' attribute, which is implemented as a deque\r\ninstance. Whenever a response is received by calling IMAP4.__next__, the library\r\nallows one of the active commands to claim the response, which is appended to\r\nthat command's queue. Unclaimed responses are placed on the common or\r\n\"unclaimed\" queue, which is an OrderedDict instance accessible via the\r\n'IMAP4.queue' attribute. The common queue is typically consumed by the NOOP\r\ncommand, like so:\r\n\r\n    # Defer all SELECT responses to the common queue for later processing\r\n    imap.select('INBOX').defer()\r\n\r\n    # Long-running FETCH command\r\n    cmd = imap.fetch(range(1, 1001), 'FULL', wait=False)\r\n    for resp in cmd:\r\n        ... # resp is guaranteed to be a FETCH response for messages 1:1000\r\n    cmd.check()\r\n\r\n    assert len(cmd.queue)  == 0  # All FETCH responses were processed\r\n    assert len(imap.queue) != 0  # SELECT responses are still waiting\r\n\r\n    # Process all SELECT responses and anything else the server sends\r\n    for resp in imap.noop():\r\n        ... # resp could be anything\r\n\r\n    assert len(imap.queue) == 0\r\n\r\nIMAP4Response\r\n-------------\r\n\r\nThe third major component of the new library is the IMAP4Response class, an\r\ninstance of which was being returned in all previous examples when iterating\r\nover IMAP4 or IMAP4Command instances. The command completion response is also\r\nassigned to 'IMAP4Command.result' attribute as soon as the command is finished.\r\nThis class is responsible for parsing and decoding server responses into a form\r\nthat is much more convenient to use in Python. Rather than explaining the parser\r\nin great detail, here are a few examples of how server responses are converted\r\nto IMAP4Response objects (formatted for clarity):\r\n\r\n    \u003e\u003e\u003e IMAP4Response('* OK Hello, this is a server greeting...')\r\n    IMAP4Response(\r\n        seq=1,\r\n        type='status',\r\n        tag='*',\r\n        status='OK',\r\n        info='Hello, this is a server greeting...',\r\n        dtype=None,\r\n        data=()\r\n    )\r\n\r\n    \u003e\u003e\u003e IMAP4Response('* THIS IS A ((DATA RESPONSE) 123 \"QUOTED STRING\")')\r\n    IMAP4Response(\r\n        seq=2,\r\n        type='data',\r\n        tag='*',\r\n        status=None,\r\n        info=None,\r\n        dtype='THIS',\r\n        data=('THIS', 'IS', 'A', [['DATA', 'RESPONSE'], 123, 'QUOTED STRING'])\r\n    )\r\n\r\n    # This is an internal representation of literals used by the library\r\n    \u003e\u003e\u003e IMAP4Response('* DATA {0} CONTAINING {1}', [b'RESPONSE', b'LITERALS'])\r\n    IMAP4Response(\r\n        seq=3,\r\n        type='data',\r\n        tag='*',\r\n        status=None,\r\n        info=None,\r\n        dtype='DATA',\r\n        data=('DATA', b'RESPONSE', 'CONTAINING', b'LITERALS')\r\n    )\r\n\r\n    \u003e\u003e\u003e IMAP4Response('TAG1 BAD [TRYCREATE] APPEND command failed')\r\n    IMAP4Response(\r\n        seq=4,\r\n        type='done',\r\n        tag='TAG1',\r\n        status='BAD',\r\n        info='APPEND command failed',\r\n        dtype='TRYCREATE',\r\n        data=('TRYCREATE',)\r\n    )\r\n\r\n    \u003e\u003e\u003e IMAP4Response('+ Please continue...')\r\n    IMAP4Response(\r\n        seq=5,\r\n        type='continue',\r\n        tag='+',\r\n        status=None,\r\n        info='Please continue...',\r\n        dtype=None,\r\n        data=()\r\n    )\r\n\r\nIn essence, the parser determines what 'type' of a response was just received\r\n(status, data, done, or continue), extracts and decodes all of its components,\r\nand assigns them to 'tag', 'status', 'info', and 'dtype' attributes. The\r\nIMAP4Response class is actually a subclass of the built-in list type, so the\r\n'data' attribute above shows the contents of the underlying list object. The\r\nfirst text data item is copied to the 'dtype' attribute in order to help\r\nidentify which command this response belongs to. For example, the response\r\n\r\n    * list (\\hasnochildren) \"/\" \"INBOX\"\r\n\r\nwill have 'dtype' set to 'LIST' (note the case change; the protocol is case-\r\ninsensitive, so dtype is always converted to upper case). While the response\r\n\r\n    * 1 fetch (uid 123)\r\n\r\nwill have 'dtype' set to 'FETCH', even though it's not the first data item.\r\n\r\nThe 'status' attribute is only set for responses beginning with OK, NO, BAD,\r\nPREAUTH, or BYE. The 'info' attribute will contain the associated human-readable\r\nexplanation of the status. If an optional response code is present (e.g.\r\n\"[TRYCREATE]\" in response 4 above), it will be decoded into the parent list\r\nobject and 'dtype' will be assigned the same way as for a data response.\r\n\r\nAll responses are assigned unique sequence IDs in the 'seq' attribute, which is\r\nused to maintain their order in the unclaimed IMAP4 response queue.\r\n\r\nIMAP4SeqSet\r\n-----------\r\n\r\nThe final library component is the IMAP4SeqSet class, which may be used\r\nimplicitly or explicitly to represent a set of message sequence numbers or UIDs.\r\nIt is a subclass of the built-in set type and provides the ability to encode and\r\ndecode sequence set strings such as '1,3,5:10,20:30'. Explicit use is when the\r\ncaller passes an IMAP4SeqSet instance as the first parameter to the FETCH,\r\nSTORE, or COPY commands. The class is used implicitly when the caller passes any\r\nother Python sequence object containing integers. The following three FETCH\r\ncommands are all identical:\r\n\r\n    imap.fetch((1, 2, 3, 4, 5, 10), 'FAST')\r\n    imap.fetch(IMAP4SeqSet('1:5,10'), 'FAST')\r\n\r\n    seqset = IMAP4SeqSet('10')\r\n    seqset.update(range(1, 6))\r\n\r\n    assert 2 in seqset\r\n    assert str(seqset) == '1:5,10'\r\n\r\n    imap.fetch(seqset, 'FAST')\r\n\r\nOne limitation of the IMAP4SeqSet is that it cannot be used for an indeterminate\r\nrange, such as '1:*'. As a result, passing a string as the first argument will\r\nnot cause a new IMAP4SeqSet instance to be created:\r\n\r\n    imap.fetch('1:5,*', 'FAST')\r\n\r\nThis has one additional downside, in that all of the following responses would\r\nbe claimed by the above command, since the library has no idea what message '*'\r\nrefers to (in the IMAP4 protocol 'N:*' and '*:N' are identical and always match\r\nthe last message in the mailbox):\r\n\r\n    * 1 FETCH (...)\r\n    * 5 FETCH (...)\r\n    * 7 FETCH (...)\r\n    * 10 FETCH (...)\r\n    * 99 FETCH (...)\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmxk%2Fpy-imaplib2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmxk%2Fpy-imaplib2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmxk%2Fpy-imaplib2/lists"}