About
=====
.. _imaplib2: https://sourceforge.net/projects/imaplib2/
.. _imaplib: https://docs.python.org/3/library/imaplib.html
.. _asyncio: https://docs.python.org/3/library/asyncio.html
.. image:: https://travis-ci.org/bamthomas/aioimaplib.png?branch=master
:alt: Build status
:target: https://travis-ci.com/bamthomas/aioimaplib
.. image:: https://coveralls.io/repos/github/bamthomas/aioimaplib/badge.svg
:target: https://coveralls.io/github/bamthomas/aioimaplib
This library is inspired by imaplib_ and imaplib2_ from Piers Lauder, Nicolas Sebrecht, Sebastian Spaeth. Some utilities functions are taken from imaplib/imaplib2 thanks to them.
The aim is to port the imaplib with asyncio_, to benefit from the sleep or treat model.
It runs with python 3.6, 3.7, 3.8, 3.9.
Example
-------
.. code-block:: python
import asyncio
from aioimaplib import aioimaplib
async def check_mailbox(host, user, password):
imap_client = aioimaplib.IMAP4_SSL(host=host)
await imap_client.wait_hello_from_server()
await imap_client.login(user, password)
res, data = await imap_client.select()
print('there is %s messages INBOX' % data[0])
await imap_client.logout()
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(check_mailbox('my.imap.server', 'user', 'pass'))
**Beware** that the IMAP4.close() function is an IMAP function that is closing the selected mailbox, thus passing from SELECTED state to AUTH state. It **does not close** the TCP connection.
The way to close TCP connection properly is to logout.
IDLE command
------------
.. _RFC2177: https://tools.ietf.org/html/rfc2177
The RFC2177_ is 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.
.. code-block:: python
async def wait_for_new_message(host, user, password):
imap_client = aioimaplib.IMAP4_SSL(host=host)
await imap_client.wait_hello_from_server()
await imap_client.login(user, password)
await imap_client.select()
idle = await imap_client.idle_start(timeout=10)
while imap_client.has_pending_idle():
msg = await imap_client.wait_server_push()
print(msg)
if msg == STOP_WAIT_SERVER_PUSH:
imap_client.idle_done()
await asyncio.wait_for(idle, 1)
await imap_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):
.. code-block:: python
async def idle_loop(host, user, password):
imap_client = aioimaplib.IMAP4_SSL(host=host, timeout=30)
await imap_client.wait_hello_from_server()
await imap_client.login(user, password)
await imap_client.select()
while True:
print((await imap_client.uid('fetch', '1:*', 'FLAGS')))
idle = await imap_client.idle_start(timeout=60)
print((await imap_client.wait_server_push()))
imap_client.idle_done()
await asyncio.wait_for(idle, 30)
Threading
---------
.. _asyncio.Event: https://docs.python.org/3.4/library/asyncio-sync.html#event
.. _asyncio.Condition: https://docs.python.org/3.4/library/asyncio-sync.html#condition
.. _supervisor: http://supervisord.org/
The IMAP4ClientProtocol class is not thread safe, it uses asyncio.Event_ and asyncio.Condition_ that 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:
.. image:: images/thread_imap_protocol.png
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 with supervisor_, 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 concurrency
------------------------
IMAP protocol allows to run some commands in parallel. Four rules are implemented to ensure responses consistency:
1. if a sync command is running, the following requests (sync or async) must wait
2. if an async command is running, same async commands (or with the same untagged response type) must wait
3. async commands can be executed in parallel
4. sync command must wait pending async commands to finish
Logging
-------
.. _howto: https://docs.python.org/3.4/howto/logging.html#configuring-logging-for-a-library
As said in the logging howto_ the logger is defined with
.. code-block:: python
logger = logging.getLogger(__name__)
Where name is 'aioimaplib.aioimaplib'. You can set the logger parameters, either by python API
.. code-block:: python
aioimaplib_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 file
.. code-block:: yaml
loggers:
...
aioimaplib.aioimaplib:
level: DEBUG
handlers: [syslog]
propagate: no
...
Tested with
-----------
- dovecot 2.2.13 on debian Jessie
- gmail with imap and SSL
- outlook with SSL
- yahoo with SSL
- free.fr with SSL
- orange.fr with SSL
- mailden.net with SSL
Develop
=======
Developers 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 run
.. code-block:: bash
virtualenv --python=python3.4 venv
source venv/bin/activate
python setup.py develop
pip install -r dev-requirements.txt
nosetests
To 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 tested
---------------
- PREAUTH
TODO
----
.. _rfc3501: https://tools.ietf.org/html/rfc3501
.. _rfc4978: https://tools.ietf.org/html/rfc4978
.. _rfc4314: https://tools.ietf.org/html/rfc4314
.. _rfc2087: https://tools.ietf.org/html/rfc2087
.. _rfc5256: https://tools.ietf.org/html/rfc5256
.. _rfc2971: https://tools.ietf.org/html/rfc2971
.. _rfc2342: https://tools.ietf.org/html/rfc2342
.. _rfc4469: https://tools.ietf.org/html/rfc4469
- 23/25 IMAP4rev1 commands are implemented from the main rfc3501_. 'STARTTLS' and 'AUTHENTICATE' are still missing.
- 'COMPRESS' from rfc4978_
- 'SETACL' 'DELETEACL' 'GETACL' 'MYRIGHTS' 'LISTRIGHTS' from ACL rfc4314_
- 'GETQUOTA': 'GETQUOTAROOT': 'SETQUOTA' from quota rfc2087_
- 'SORT' and 'THREAD' from the rfc5256_
- 'ID' from the rfc2971_
- 'NAMESPACE' from rfc2342_
- 'CATENATE' from rfc4469_
- tests with other servers
If it goes wrong
----------------
Sometimes you break things and you don't understand what's going on (I always do). For this library I have two related tools:
.. role:: bash(code)
:language: bash
- ngrep on the imap test port: :bash:`sudo ngrep -d lo port 12345`
- activate debug logs changing INFO to DEBUG at the top of the mock server and the aioimaplib
Changes
=======
V1.0.0
------
After 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 command
V0.9.0
------
Please 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 lines
V0.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 version
V0.7.18
-------
- [aiolib] fix the fix python 3.7 don't use the 7.17
V0.7.17
-------
- [aiolib] fix loading issue when importing aioimaplib with python 3.7
V0.7.16
-------
merge 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_future
V0.7.15
-------
- [aiolib] replace 'async' by is_async for python 3.7 cf #42
- [aiolib] Add ID command
- [aiolib] Add NAMESPACE command
V0.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 dialog
V0.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 #27
V0.7.11
-------
- [aiolib] adds rfc6851 move
V0.7.10
-------
- [aiolib] adds IMAP4.has_capability function
V0.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_responses
V0.7.8
------
- [aiolib] idle : added an idle_waiter for an event based idle loop
V0.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 uid
V0.7.6
------
- [aiolib] idle : added loop methods
V0.7.5
------
- [aiolib][fix] it's up to the user to send idle DONE
V0.7.4
------
- [aiolib] timeout for idle of 29 minutes + timeout of wait_server_push doesn't raise TimeoutException
V0.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 command
V0.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.py
V0.7.1
------
- [refactor] adding incomplete line before calling _handle_responses
V0.7.0
------
- [fix] generalization of literal treatment
- do not filter exists line for 'select' command (breaks the API). To retrieve unread mails with select, use
aioimaplib.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 regexp
V0.6.1
------
- [fix] issue #17 "Error fetch uid param"
V0.6.0
------
- moved timeout handling at the Command level and not IMAP4 client for fetch as proposed by @cyberlis in https://github.com/bamthomas/aioimaplib/pull/16
V0.5.20
-------
- fix : issue #15 https://github.com/bamthomas/aioimaplib/issues/15 This 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.19
-------
- tests : [revert] add_charset to much intrusive when running a test suite
V0.5.18
-------
- tests : body text was not base64 encoded even if the header said so
V0.5.17
-------
- tests : mail_from parameter from Mail.create should handle mail@host, <mail@host>, Name <mail@host>
V0.5.16
-------
- tests : added better encoding handling and message building in Mail.create
V0.5.15
-------
- tests : added message_id as Mail.create parameter for testing
V0.5.14
-------
- tests : extract Mail.create_binary for convenience
V0.5.13
-------
- fix : trailing whitespace bug causing "BAD Could not parse command" using gmail/IDLE
- fix : stop adding a space for the prefix 'UID ' -> 'UID'
V0.5.12
-------
- fix : issue #12 Not properly buffering newlines for incomplete lines
- fix : imapserver with status of an inexistant mailbox
- fix : remove offset problem with strip() modifying length of read data
- fix : remove 'unknown data received' logs if line is empty
V0.5.11
-------
- remove hard coded logging config
- doc : added logging settings
V0.5.10
-------
- added rfc5032 'within' function to server and tests for aiolib (it is only YOUNGER/OLDER arguments)
V0.5.9
------
- pushing continuation in the queue when idled
V0.5.8
------
- added a stop waiting server push function to interupt yield from queue.get
V0.5.7
------
- server send still here every IDLE_STILL_HERE_PERIOD_SECONDS to client when idle
- fix when server was lauched with main, loop is already running
V0.5.6
------
- fix doc
- fix imapserver main (needs a asyncio.loop.run_forever())
V0.5.5
------
- fix issues with coroutines in uid command
- documentation
- remove PARTIAL, PROXYAUTH, SETANNOTATION and GETANNOTATION commands
V0.5.4
------
- refactor: treating response as we read the imap server responses for a better reading
- doc
- removing tests from package
- publish on pypi
- added coverall
V0.5.3
------
- fix aioimaplib bug when receiving chunked fetch data
- do not abort when receiving unsollicited data from server
V0.5.2
------
- build CI environment
- license GPL v3.0
V0.5.1
------
- added APPEND command
- fix usernames can have '@' for mockimapserver
- server can handle SEARCH with CHARSET opt parameter (but ignores it)
V0.5
----
- added 11 new imap commands
- added imap command synchronizing
- refactor
- documentation
V0.1
----
- init project with mockimapserver
- project files
- 11 imap commands
Raw data
{
"_id": null,
"home_page": "https://github.com/bamthomas/aioimaplib",
"name": "aioimaplib",
"maintainer": "",
"docs_url": null,
"requires_python": "",
"maintainer_email": "",
"keywords": "asyncio mail imap",
"author": "Bruno Thomas",
"author_email": "bruno@barreverte.fr",
"download_url": "https://files.pythonhosted.org/packages/c1/4f/2e9929546e83a52a7e664e33313756efd3f3a011f54a54aed0f374e5db2f/aioimaplib-1.0.1.tar.gz",
"platform": null,
"description": "About\n=====\n.. _imaplib2: https://sourceforge.net/projects/imaplib2/\n.. _imaplib: https://docs.python.org/3/library/imaplib.html\n.. _asyncio: https://docs.python.org/3/library/asyncio.html\n\n.. image:: https://travis-ci.org/bamthomas/aioimaplib.png?branch=master\n :alt: Build status\n :target: https://travis-ci.com/bamthomas/aioimaplib\n\n.. image:: https://coveralls.io/repos/github/bamthomas/aioimaplib/badge.svg\n :target: https://coveralls.io/github/bamthomas/aioimaplib\n\n\nThis library is inspired by imaplib_ and imaplib2_ from Piers Lauder, Nicolas Sebrecht, Sebastian Spaeth. Some utilities functions are taken from imaplib/imaplib2 thanks to them.\n\nThe aim is to port the imaplib with asyncio_, to benefit from the sleep or treat model.\n\nIt runs with python 3.6, 3.7, 3.8, 3.9.\n\nExample\n-------\n\n.. code-block:: python\n\n import asyncio\n from aioimaplib import aioimaplib\n\n\n async def check_mailbox(host, user, password):\n imap_client = aioimaplib.IMAP4_SSL(host=host)\n await imap_client.wait_hello_from_server()\n\n await imap_client.login(user, password)\n\n res, data = await imap_client.select()\n print('there is %s messages INBOX' % data[0])\n\n await imap_client.logout()\n\n\n if __name__ == '__main__':\n loop = asyncio.get_event_loop()\n loop.run_until_complete(check_mailbox('my.imap.server', 'user', 'pass'))\n\n**Beware** that the IMAP4.close() function is an IMAP function that is closing the selected mailbox, thus passing from SELECTED state to AUTH state. It **does not close** the TCP connection.\nThe way to close TCP connection properly is to logout.\n\nIDLE command\n------------\n.. _RFC2177: https://tools.ietf.org/html/rfc2177\n\nThe RFC2177_ is 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.\n\n.. code-block:: python\n\n async def wait_for_new_message(host, user, password):\n imap_client = aioimaplib.IMAP4_SSL(host=host)\n await imap_client.wait_hello_from_server()\n\n await imap_client.login(user, password)\n await imap_client.select()\n\n idle = await imap_client.idle_start(timeout=10)\n while imap_client.has_pending_idle():\n msg = await imap_client.wait_server_push()\n print(msg)\n if msg == STOP_WAIT_SERVER_PUSH:\n imap_client.idle_done()\n await asyncio.wait_for(idle, 1)\n\n await imap_client.logout()\n\n if __name__ == '__main__':\n loop = asyncio.get_event_loop()\n loop.run_until_complete(wait_for_new_message('my.imap.server', 'user', 'pass'))\n\nOr in a more event based style (the IDLE command is closed at each message from server):\n\n.. code-block:: python\n\n async def idle_loop(host, user, password):\n imap_client = aioimaplib.IMAP4_SSL(host=host, timeout=30)\n await imap_client.wait_hello_from_server()\n\n await imap_client.login(user, password)\n await imap_client.select()\n\n while True:\n print((await imap_client.uid('fetch', '1:*', 'FLAGS')))\n\n idle = await imap_client.idle_start(timeout=60)\n print((await imap_client.wait_server_push()))\n\n imap_client.idle_done()\n await asyncio.wait_for(idle, 30)\n\nThreading\n---------\n.. _asyncio.Event: https://docs.python.org/3.4/library/asyncio-sync.html#event\n.. _asyncio.Condition: https://docs.python.org/3.4/library/asyncio-sync.html#condition\n.. _supervisor: http://supervisord.org/\n\nThe IMAP4ClientProtocol class is not thread safe, it uses asyncio.Event_ and asyncio.Condition_ that are not thread safe, and state change for pending commands is not locked.\n\nIt is possible to use threads but each IMAP4ClientProtocol instance should run in the same thread:\n\n.. image:: images/thread_imap_protocol.png\n\nEach color rectangle is an IMAP4ClientProtocol instance piece of code executed by the thread asyncio loop until it reaches a yield, waiting on I/O.\n\nFor example, it is possible to launch 4 mono-threaded mail-fetcher processes on a 4 cores server with supervisor_, and use a distribution function like len(email) % (process_num) or whatever to share equally a mail account list between the 4 processes.\n\nIMAP command concurrency\n------------------------\n\nIMAP protocol allows to run some commands in parallel. Four rules are implemented to ensure responses consistency:\n\n1. if a sync command is running, the following requests (sync or async) must wait\n2. if an async command is running, same async commands (or with the same untagged response type) must wait\n3. async commands can be executed in parallel\n4. sync command must wait pending async commands to finish\n\nLogging\n-------\n.. _howto: https://docs.python.org/3.4/howto/logging.html#configuring-logging-for-a-library\n\nAs said in the logging howto_ the logger is defined with\n\n.. code-block:: python\n\n logger = logging.getLogger(__name__)\n\n\nWhere name is 'aioimaplib.aioimaplib'. You can set the logger parameters, either by python API\n\n.. code-block:: python\n\n aioimaplib_logger = logging.getLogger('aioimaplib.aioimaplib')\n sh = logging.StreamHandler()\n sh.setLevel(logging.DEBUG)\n sh.setFormatter(logging.Formatter(\"%(asctime)s %(levelname)s [%(module)s:%(lineno)d] %(message)s\"))\n aioimaplib_logger.addHandler(sh)\n\nOr loading config file (for example with logging.config.dictConfig(yaml.load(file))) with this piece of yaml file\n\n.. code-block:: yaml\n\n loggers:\n ...\n aioimaplib.aioimaplib:\n level: DEBUG\n handlers: [syslog]\n propagate: no\n ...\n\nTested with\n-----------\n\n- dovecot 2.2.13 on debian Jessie\n- gmail with imap and SSL\n- outlook with SSL\n- yahoo with SSL\n- free.fr with SSL\n- orange.fr with SSL\n- mailden.net with SSL\n\nDevelop\n=======\n\nDevelopers are welcome! If you want to improve it, fix bugs, test it with other IMAP servers, give feedback, thank you for it.\n\nTo develop, just run\n\n.. code-block:: bash\n\n virtualenv --python=python3.4 venv\n source venv/bin/activate\n python setup.py develop\n pip install -r dev-requirements.txt\n nosetests\n\nTo add an imaplib or imaplib2 command you can :\n\n- 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;\n- then add the function to the aioimaplib doing almost the same test than above but the async way in test_aioimaplib.py.\n\nNot unit tested\n---------------\n- PREAUTH\n\nTODO\n----\n.. _rfc3501: https://tools.ietf.org/html/rfc3501\n.. _rfc4978: https://tools.ietf.org/html/rfc4978\n.. _rfc4314: https://tools.ietf.org/html/rfc4314\n.. _rfc2087: https://tools.ietf.org/html/rfc2087\n.. _rfc5256: https://tools.ietf.org/html/rfc5256\n.. _rfc2971: https://tools.ietf.org/html/rfc2971\n.. _rfc2342: https://tools.ietf.org/html/rfc2342\n.. _rfc4469: https://tools.ietf.org/html/rfc4469\n\n- 23/25 IMAP4rev1 commands are implemented from the main rfc3501_. 'STARTTLS' and 'AUTHENTICATE' are still missing.\n- 'COMPRESS' from rfc4978_\n- 'SETACL' 'DELETEACL' 'GETACL' 'MYRIGHTS' 'LISTRIGHTS' from ACL rfc4314_\n- 'GETQUOTA': 'GETQUOTAROOT': 'SETQUOTA' from quota rfc2087_\n- 'SORT' and 'THREAD' from the rfc5256_\n- 'ID' from the rfc2971_\n- 'NAMESPACE' from rfc2342_\n- 'CATENATE' from rfc4469_\n- tests with other servers\n\nIf it goes wrong\n----------------\nSometimes you break things and you don't understand what's going on (I always do). For this library I have two related tools:\n\n.. role:: bash(code)\n :language: bash\n\n- ngrep on the imap test port: :bash:`sudo ngrep -d lo port 12345`\n- activate debug logs changing INFO to DEBUG at the top of the mock server and the aioimaplib\n\n\nChanges\n=======\n\n\nV1.0.0\n------\n\nAfter 5 years and 420 usages let's switch to 1.0.0.\n\n- [aiolib] adds python 3.10 (but python 3.10 is failing on downloading interpretor)\n- [aiolib] adds getquotaroot command\n\nV0.9.0\n------\nPlease note that this release changes the API : response lines are now all of bytes type (and not in string).\n\n- [aiolib] compatible with python 3.9\n- [aiolib] avoid having distinct types (str/bytes) in response lines\n\nV0.8.0\n-------\n- [aiolib] adds type hints\n- [aiolib] use async/await syntax (remove support for python 3.4)\n- [aiolib] fixes issue #30 on idle race condition\n- [aiolib] fixes the allowed version\n\nV0.7.18\n-------\n- [aiolib] fix the fix python 3.7 don't use the 7.17\n\n\nV0.7.17\n-------\n- [aiolib] fix loading issue when importing aioimaplib with python 3.7\n\n\nV0.7.16\n-------\nmerge from darkrain42\n\n- [aiolib] fix interaction between idle loop and server keepalive\n- [aiolib] fix cert verification on debian buster\n- [test] fix imapserver literal handling on Python 3.6.5+\n- [test] tests: Replace calls to asyncio.async with asyncio.ensure_future\n\n\nV0.7.15\n-------\n- [aiolib] replace 'async' by is_async for python 3.7 cf #42\n- [aiolib] Add ID command\n- [aiolib] Add NAMESPACE command\n\nV0.7.14\n-------\n- [fix] when there is ::1 as localhost in /etc/hosts in a docker container\n- [test] tests with SSL\n- [aiolib] quote password properly in IMAP dialog\n\nV0.7.13\n-------\n- [aiolib] adds a connection lost callback\n- [test] imapserver : added APPENDUID response for APPEND cmd\n- [test][fix] imapserver append should add to the connected user mb\n- [test] imapserver : more accurate building of message headers (using python email module)\n\nV0.7.12\n-------\n- [aiolib] fix issue #24\n- [aiolib] fix issue #27\n\nV0.7.11\n-------\n- [aiolib] adds rfc6851 move\n\nV0.7.10\n-------\n- [aiolib] adds IMAP4.has_capability function\n\nV0.7.9\n------\n- [aiolib] adds uncomplete fetch command with uncomplete line\n- [aiolib] adds uncomplete fetch command BODY without literal\n- [aiolib] adds rfc4315 uidplus : expunge with uids\n- [test] handles uidset better in imap server\n- [test] refactor testing IMAP4ClientProtocol.data_received instead of _handle_responses\n\nV0.7.8\n------\n- [aiolib] idle : added an idle_waiter for an event based idle loop\n\nV0.7.7\n------\n- [aiolib] do not filter exists line for examine command\n- [aiolib] idle : wait for the end of data frame before pushing into the queue\n- [test] imapserver enhancements : accepts sequence sets/returns UID when fetch by uid\n\nV0.7.6\n------\n- [aiolib] idle : added loop methods\n\nV0.7.5\n------\n- [aiolib][fix] it's up to the user to send idle DONE\n\nV0.7.4\n------\n- [aiolib] timeout for idle of 29 minutes + timeout of wait_server_push doesn't raise TimeoutException\n\nV0.7.3\n------\n- [aiolib] added timeout for wait_server_push of 29 minutes\n- [aiolib] Remove imap's Continuation from server when connection is idled. Provide imapserver.wait_state to wait for idle in tests\n- [test][refactor] Replace WithIMapServer inheritance by mixin combinations between ClockTestCase/TestCase and WithIMapServer\n- [test] Allow to send a html email\n- [fix] handling untagged responses with noop async command\n\n\nV0.7.2\n------\n- [fix] bug when incomplete literal occured before a tagged status line\n- [tests] imapserver search with uid range\n- [tests] better fetch request handling\n- [log] Limit partials' log to 100 characters\n- [build] Add tests' requires in setup.py\n\nV0.7.1\n------\n- [refactor] adding incomplete line before calling _handle_responses\n\nV0.7.0\n------\n- [fix] generalization of literal treatment\n- do not filter exists line for 'select' command (breaks the API). To retrieve unread mails with select, use\n aioimaplib.extract_exists((yield from imap_client.select()) instead of 'yield from imap_client.select()[0]'\n\nV0.6.2\n------\n- [fix] added '$' and ';' for fetch message with litteral regexp \n\nV0.6.1\n------\n- [fix] issue #17 \"Error fetch uid param\"\n\nV0.6.0\n------\n- moved timeout handling at the Command level and not IMAP4 client for fetch as proposed by @cyberlis in https://github.com/bamthomas/aioimaplib/pull/16\n\nV0.5.20\n-------\n- fix : issue #15 https://github.com/bamthomas/aioimaplib/issues/15 This 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.\n\nV0.5.19\n-------\n- tests : [revert] add_charset to much intrusive when running a test suite \n\nV0.5.18\n-------\n- tests : body text was not base64 encoded even if the header said so\n\nV0.5.17\n-------\n- tests : mail_from parameter from Mail.create should handle mail@host, <mail@host>, Name <mail@host>\n\nV0.5.16\n-------\n- tests : added better encoding handling and message building in Mail.create \n\nV0.5.15\n-------\n- tests : added message_id as Mail.create parameter for testing \n\nV0.5.14\n-------\n- tests : extract Mail.create_binary for convenience\n\nV0.5.13\n-------\n- fix : trailing whitespace bug causing \"BAD Could not parse command\" using gmail/IDLE\n- fix : stop adding a space for the prefix 'UID ' -> 'UID'\n\nV0.5.12\n-------\n- fix : issue #12 Not properly buffering newlines for incomplete lines\n- fix : imapserver with status of an inexistant mailbox\n- fix : remove offset problem with strip() modifying length of read data\n- fix : remove 'unknown data received' logs if line is empty\n\nV0.5.11\n-------\n- remove hard coded logging config\n- doc : added logging settings\n\nV0.5.10\n-------\n- added rfc5032 'within' function to server and tests for aiolib (it is only YOUNGER/OLDER arguments)\n\nV0.5.9\n------\n- pushing continuation in the queue when idled\n\nV0.5.8\n------\n- added a stop waiting server push function to interupt yield from queue.get\n\nV0.5.7\n------\n- server send still here every IDLE_STILL_HERE_PERIOD_SECONDS to client when idle\n- fix when server was lauched with main, loop is already running\n\nV0.5.6\n------\n- fix doc\n- fix imapserver main (needs a asyncio.loop.run_forever())\n\nV0.5.5\n------\n- fix issues with coroutines in uid command\n- documentation\n- remove PARTIAL, PROXYAUTH, SETANNOTATION and GETANNOTATION commands\n\nV0.5.4\n------\n- refactor: treating response as we read the imap server responses for a better reading\n- doc\n- removing tests from package\n- publish on pypi\n- added coverall\n\nV0.5.3\n------\n- fix aioimaplib bug when receiving chunked fetch data\n- do not abort when receiving unsollicited data from server\n\nV0.5.2\n------\n- build CI environment\n- license GPL v3.0\n\nV0.5.1\n------\n- added APPEND command\n- fix usernames can have '@' for mockimapserver\n- server can handle SEARCH with CHARSET opt parameter (but ignores it)\n\nV0.5\n----\n- added 11 new imap commands\n- added imap command synchronizing\n- refactor\n- documentation\n\nV0.1\n----\n- init project with mockimapserver\n- project files\n- 11 imap commands\n\n\n",
"bugtrack_url": null,
"license": "GPL-3.0",
"summary": "Python asyncio IMAP4rev1 client library",
"version": "1.0.1",
"split_keywords": [
"asyncio",
"mail",
"imap"
],
"urls": [
{
"comment_text": "",
"digests": {
"md5": "e1a61e4c499851cedc457dba4404ed44",
"sha256": "7707e7f18e7dd99d1626fd652ebf06d75abf7bf128ef286a84b270abb8c79360"
},
"downloads": -1,
"filename": "aioimaplib-1.0.1-py3.9.egg",
"has_sig": false,
"md5_digest": "e1a61e4c499851cedc457dba4404ed44",
"packagetype": "bdist_egg",
"python_version": "1.0.1",
"requires_python": null,
"size": 16308,
"upload_time": "2022-09-16T22:39:33",
"upload_time_iso_8601": "2022-09-16T22:39:33.282562Z",
"url": "https://files.pythonhosted.org/packages/10/5f/191dfea679f4065dcbdc2159f00fe08070340bff06f44333ab9d57ab8b9e/aioimaplib-1.0.1-py3.9.egg",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"md5": "8d43f6fe2786dbad2823345e300e5373",
"sha256": "ebf8387a8febf4d4ae21b35a54cc7c9f4b77c3a7132e3e50c7d6a14993b07bf3"
},
"downloads": -1,
"filename": "aioimaplib-1.0.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "8d43f6fe2786dbad2823345e300e5373",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 27692,
"upload_time": "2022-09-16T22:39:30",
"upload_time_iso_8601": "2022-09-16T22:39:30.385067Z",
"url": "https://files.pythonhosted.org/packages/27/35/fc1f489623ca1b0849dae3cf842d04bd7d47c4c012790df104fd72aa4f6b/aioimaplib-1.0.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"md5": "fbbc15d4b5712da15a795d345605f780",
"sha256": "287fd8a2386f2ff301ac4f8479633292dae80e223e8e40ce2f38038784822afa"
},
"downloads": -1,
"filename": "aioimaplib-1.0.1.tar.gz",
"has_sig": false,
"md5_digest": "fbbc15d4b5712da15a795d345605f780",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 33245,
"upload_time": "2022-09-16T22:39:36",
"upload_time_iso_8601": "2022-09-16T22:39:36.645879Z",
"url": "https://files.pythonhosted.org/packages/c1/4f/2e9929546e83a52a7e664e33313756efd3f3a011f54a54aed0f374e5db2f/aioimaplib-1.0.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2022-09-16 22:39:36",
"github": true,
"gitlab": false,
"bitbucket": false,
"github_user": "bamthomas",
"github_project": "aioimaplib",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"tox": true,
"lcname": "aioimaplib"
}