Package: aioimaplib
AboutThis library is inspired byimaplibandimaplib2from Piers Lauder, Nicolas Sebrecht, Sebastian Spaeth. Some utilities functions are taken from imaplib/imaplib2 thanks to them.The aim is to port the imaplib withasyncio, to benefit from the sleep or treat model.It runs with python 3.6, 3.7, 3.8, 3.9.Exampleimportasynciofromaioimaplibimportaioimaplibasyncdefcheck_mailbox(host,user,password):imap_client=aioimaplib.IMAP4_SSL(host=host)awaitimap_client.wait_hello_from_server()awaitimap_client.login(user,password)res,data=awaitimap_client.select()print('there is%smessages INBOX'%data[0])awaitimap_client.logout()if__name__=='__main__':loop=asyncio.get_event_loop()loop.run_until_complete(check_mailbox('my.imap.server','user','pass'))Bewarethat the IMAP4.close() function is an IMAP function that is closing the selected mailbox, thus passing from SELECTED state to AUTH state. Itdoes not closethe TCP connection.
The way to close TCP connection properly is to logout.IDLE commandTheRFC2177is implemented, to be able to wait for new mail messages without using CPU. The responses are pushed in an async queue, and it is possible to read them in real time. To leave the IDLE mode, it is necessary to send a “DONE” command to the server.asyncdefwait_for_new_message(host,user,password):imap_client=aioimaplib.IMAP4_SSL(host=host)awaitimap_client.wait_hello_from_server()awaitimap_client.login(user,password)awaitimap_client.select()idle=awaitimap_client.idle_start(timeout=10)whileimap_client.has_pending_idle():msg=awaitimap_client.wait_server_push()print(msg)ifmsg==STOP_WAIT_SERVER_PUSH:imap_client.idle_done()awaitasyncio.wait_for(idle,1)awaitimap_client.logout()if__name__=='__main__':loop=asyncio.get_event_loop()loop.run_until_complete(wait_for_new_message('my.imap.server','user','pass'))Or in a more event based style (the IDLE command is closed at each message from server):asyncdefidle_loop(host,user,password):imap_client=aioimaplib.IMAP4_SSL(host=host,timeout=30)awaitimap_client.wait_hello_from_server()awaitimap_client.login(user,password)awaitimap_client.select()whileTrue:print((awaitimap_client.uid('fetch','1:*','FLAGS')))idle=awaitimap_client.idle_start(timeout=60)print((awaitimap_client.wait_server_push()))imap_client.idle_done()awaitasyncio.wait_for(idle,30)ThreadingThe IMAP4ClientProtocol class is not thread safe, it usesasyncio.Eventandasyncio.Conditionthat are not thread safe, and state change for pending commands is not locked.It is possible to use threads but each IMAP4ClientProtocol instance should run in the same thread:Each color rectangle is an IMAP4ClientProtocol instance piece of code executed by the thread asyncio loop until it reaches a yield, waiting on I/O.For example, it is possible to launch 4 mono-threaded mail-fetcher processes on a 4 cores server withsupervisor, and use a distribution function like len(email) % (process_num) or whatever to share equally a mail account list between the 4 processes.IMAP command concurrencyIMAP protocol allows to run some commands in parallel. Four rules are implemented to ensure responses consistency:if a sync command is running, the following requests (sync or async) must waitif an async command is running, same async commands (or with the same untagged response type) must waitasync commands can be executed in parallelsync command must wait pending async commands to finishLoggingAs said in the logginghowtothe logger is defined withlogger=logging.getLogger(__name__)Where name is ‘aioimaplib.aioimaplib’. You can set the logger parameters, either by python APIaioimaplib_logger=logging.getLogger('aioimaplib.aioimaplib')sh=logging.StreamHandler()sh.setLevel(logging.DEBUG)sh.setFormatter(logging.Formatter("%(asctime)s%(levelname)s[%(module)s:%(lineno)d]%(message)s"))aioimaplib_logger.addHandler(sh)Or loading config file (for example with logging.config.dictConfig(yaml.load(file))) with this piece of yaml fileloggers:...aioimaplib.aioimaplib:level:DEBUGhandlers:[syslog]propagate:no...Tested withdovecot 2.2.13 on debian Jessiegmail with imap and SSLoutlook with SSLyahoo with SSLfree.fr with SSLorange.fr with SSLmailden.net with SSLDevelopDevelopers are welcome! If you want to improve it, fix bugs, test it with other IMAP servers, give feedback, thank you for it.To develop, just runvirtualenv--python=python3.4venvsourcevenv/bin/activatepythonsetup.pydeveloppipinstall-rdev-requirements.txtnosetestsTo add an imaplib or imaplib2 command you can :add the function to the testing imapserver with a new imaplib or imaplib2 server test, i.e. test_imapserver_imaplib.py or test_imapserver_imaplib2.py respectively;then add the function to the aioimaplib doing almost the same test than above but the async way in test_aioimaplib.py.Not unit testedPREAUTHTODO23/25 IMAP4rev1 commands are implemented from the mainrfc3501. ‘STARTTLS’ and ‘AUTHENTICATE’ are still missing.‘COMPRESS’ fromrfc4978‘SETACL’ ‘DELETEACL’ ‘GETACL’ ‘MYRIGHTS’ ‘LISTRIGHTS’ from ACLrfc4314‘GETQUOTA’: ‘GETQUOTAROOT’: ‘SETQUOTA’ from quotarfc2087‘SORT’ and ‘THREAD’ from therfc5256‘ID’ from therfc2971‘NAMESPACE’ fromrfc2342‘CATENATE’ fromrfc4469tests with other serversIf it goes wrongSometimes you break things and you don’t understand what’s going on (I always do). For this library I have two related tools:ngrep on the imap test port:sudongrep-dloport12345activate debug logs changing INFO to DEBUG at the top of the mock server and the aioimaplibChangesV1.0.0After 5 years and 420 usages let’s switch to 1.0.0.[aiolib] adds python 3.10 (but python 3.10 is failing on downloading interpretor)[aiolib] adds getquotaroot commandV0.9.0Please note that this release changes the API : response lines are now all of bytes type (and not in string).[aiolib] compatible with python 3.9[aiolib] avoid having distinct types (str/bytes) in response linesV0.8.0[aiolib] adds type hints[aiolib] use async/await syntax (remove support for python 3.4)[aiolib] fixes issue #30 on idle race condition[aiolib] fixes the allowed versionV0.7.18[aiolib] fix the fix python 3.7 don’t use the 7.17V0.7.17[aiolib] fix loading issue when importing aioimaplib with python 3.7V0.7.16merge from darkrain42[aiolib] fix interaction between idle loop and server keepalive[aiolib] fix cert verification on debian buster[test] fix imapserver literal handling on Python 3.6.5+[test] tests: Replace calls to asyncio.async with asyncio.ensure_futureV0.7.15[aiolib] replace ‘async’ by is_async for python 3.7 cf #42[aiolib] Add ID command[aiolib] Add NAMESPACE commandV0.7.14[fix] when there is ::1 as localhost in /etc/hosts in a docker container[test] tests with SSL[aiolib] quote password properly in IMAP dialogV0.7.13[aiolib] adds a connection lost callback[test] imapserver : added APPENDUID response for APPEND cmd[test][fix] imapserver append should add to the connected user mb[test] imapserver : more accurate building of message headers (using python email module)V0.7.12[aiolib] fix issue #24[aiolib] fix issue #27V0.7.11[aiolib] adds rfc6851 moveV0.7.10[aiolib] adds IMAP4.has_capability functionV0.7.9[aiolib] adds uncomplete fetch command with uncomplete line[aiolib] adds uncomplete fetch command BODY without literal[aiolib] adds rfc4315 uidplus : expunge with uids[test] handles uidset better in imap server[test] refactor testing IMAP4ClientProtocol.data_received instead of _handle_responsesV0.7.8[aiolib] idle : added an idle_waiter for an event based idle loopV0.7.7[aiolib] do not filter exists line for examine command[aiolib] idle : wait for the end of data frame before pushing into the queue[test] imapserver enhancements : accepts sequence sets/returns UID when fetch by uidV0.7.6[aiolib] idle : added loop methodsV0.7.5[aiolib][fix] it’s up to the user to send idle DONEV0.7.4[aiolib] timeout for idle of 29 minutes + timeout of wait_server_push doesn’t raise TimeoutExceptionV0.7.3[aiolib] added timeout for wait_server_push of 29 minutes[aiolib] Remove imap’s Continuation from server when connection is idled. Provide imapserver.wait_state to wait for idle in tests[test][refactor] Replace WithIMapServer inheritance by mixin combinations between ClockTestCase/TestCase and WithIMapServer[test] Allow to send a html email[fix] handling untagged responses with noop async commandV0.7.2[fix] bug when incomplete literal occured before a tagged status line[tests] imapserver search with uid range[tests] better fetch request handling[log] Limit partials’ log to 100 characters[build] Add tests’ requires in setup.pyV0.7.1[refactor] adding incomplete line before calling _handle_responsesV0.7.0[fix] generalization of literal treatmentdo not filter exists line for ‘select’ command (breaks the API). To retrieve unread mails with select, useaioimaplib.extract_exists((yield from imap_client.select()) instead of ‘yield from imap_client.select()[0]’V0.6.2[fix] added ‘$’ and ‘;’ for fetch message with litteral regexpV0.6.1[fix] issue #17 “Error fetch uid param”V0.6.0moved timeout handling at the Command level and not IMAP4 client for fetch as proposed by @cyberlis inhttps://github.com/bamthomas/aioimaplib/pull/16V0.5.20fix : issue #15https://github.com/bamthomas/aioimaplib/issues/15This will break the API for FETCH with emails BODY : now the first line is the server FETCH server response line. The messages are between 1 and end of Response.lines list.V0.5.19tests : [revert] add_charset to much intrusive when running a test suiteV0.5.18tests : body text was not base64 encoded even if the header said soV0.5.17tests : mail_from parameter from Mail.create should handlemail@host, <mail@host>, Name <mail@host>V0.5.16tests : added better encoding handling and message building in Mail.createV0.5.15tests : added message_id as Mail.create parameter for testingV0.5.14tests : extract Mail.create_binary for convenienceV0.5.13fix : trailing whitespace bug causing “BAD Could not parse command” using gmail/IDLEfix : stop adding a space for the prefix ‘UID ‘ -> ‘UID’V0.5.12fix : issue #12 Not properly buffering newlines for incomplete linesfix : imapserver with status of an inexistant mailboxfix : remove offset problem with strip() modifying length of read datafix : remove ‘unknown data received’ logs if line is emptyV0.5.11remove hard coded logging configdoc : added logging settingsV0.5.10added rfc5032 ‘within’ function to server and tests for aiolib (it is only YOUNGER/OLDER arguments)V0.5.9pushing continuation in the queue when idledV0.5.8added a stop waiting server push function to interupt yield from queue.getV0.5.7server send still here every IDLE_STILL_HERE_PERIOD_SECONDS to client when idlefix when server was lauched with main, loop is already runningV0.5.6fix docfix imapserver main (needs a asyncio.loop.run_forever())V0.5.5fix issues with coroutines in uid commanddocumentationremove PARTIAL, PROXYAUTH, SETANNOTATION and GETANNOTATION commandsV0.5.4refactor: treating response as we read the imap server responses for a better readingdocremoving tests from packagepublish on pypiadded coverallV0.5.3fix aioimaplib bug when receiving chunked fetch datado not abort when receiving unsollicited data from serverV0.5.2build CI environmentlicense GPL v3.0V0.5.1added APPEND commandfix usernames can have ‘@’ for mockimapserverserver can handle SEARCH with CHARSET opt parameter (but ignores it)V0.5added 11 new imap commandsadded imap command synchronizingrefactordocumentationV0.1init project with mockimapserverproject files11 imap commands