bamthomas / aioimaplib Goto Github PK
View Code? Open in Web Editor NEWPython asyncio IMAP4rev1 client library
License: GNU General Public License v3.0
Python asyncio IMAP4rev1 client library
License: GNU General Public License v3.0
Hi,
I use the idle command to get notified for new messages. I restart the idle command every x minutes. When there is server status update after the 'DONE' is send, it is appended to the response to the 'IDLE' command.
[aioimaplib.py:289 data_received] Received : b'+ idling\r\n* 418 FETCH (FLAGS ())\r\n* 420 FETCH (FLAGS (\Seen))\r\n* 421 FETCH (FLAGS (\Seen))\r\n* 451 FETCH (FLAGS ())\r\n'
All these lines are added to the IdleCommand buffer and the answer is consumed from die aioimaplib somewhere. The changed Flags are beeing lost.
Here is a Solution:
We should change line 220 from
self.queue.put_nowait(copy(self.buffer))
to
for item in self.buffer:
self.queue.put_nowait(copy(item))
so each item from the buffer (e.g. each line of the response) is added individually to queue.
Because I'm not so familiar with the code I'm not sure if this change breaks something. Can someone please comment on this?
Thanks!
I've run into an annoying bug that I'm not sure how to fix. Sometimes the data fed to _handle_responses
is like so: ...QBo+puGrxMuda6Cz23F6J7p\r\n\r\n)\r\nP
. The stray P
at the end of the data is the start of the TAG for the next command, in this case self.tagpre
is PHMD
.
The problem is that this trailing byte is buffered and sent to _handle_line
all by itself, which causes it to be rejected. Then when the next chunk of data from the server is received it is rejected because the tag is found to start with HMD
, not PHMD
.
Perhaps using StreamReaderProtocol
rather than Protocol
would be a better fit?
Hi
In a coroutine, I have
imap_client = aioimaplib.IMAP4_SSL(host=host, timeout=30)
yield from imap_client.wait_hello_from_server()
yield from imap_client.login(user, password)
yield from imap_client.select()
while True:
#print((yield from imap_client.uid('fetch', '1:*', 'FLAGS')))
idle = yield from imap_client.idle_start(timeout=6)
print((yield from imap_client.wait_server_push()))
imap_client.idle_done()
yield from asyncio.wait_for(idle, 30)
sometimes I got the following debug:
[…]
2018-01-18 14:23:15,478 DEBUG [aioimaplib:360] Sending : b'DONE\r\n'
2018-01-18 14:23:15,514 DEBUG [aioimaplib:295] Received : b'GJDH3 OK Idle completed.\r\n'
2018-01-18 14:23:15,514 DEBUG [aioimaplib:581] tagged status GJDH3 OK Idle completed.
2018-01-18 14:23:15,514 DEBUG [aioimaplib:360] Sending : b'GJDH4 IDLE\r\n'
2018-01-18 14:23:15,550 DEBUG [aioimaplib:295] Received : b'+ idling\r\n'
2018-01-18 14:23:15,550 DEBUG [aioimaplib:610] continuation line appended to pending sync command GJDH4 IDLE : + idling
stop_wait_server_push
2018-01-18 14:23:21,515 DEBUG [aioimaplib:360] Sending : b'DONE\r\n'
2018-01-18 14:23:21,641 DEBUG [aioimaplib:295] Received : b'* 493 EXISTS\r\n* 1 RECENT\r\n'
2018-01-18 14:23:21,674 DEBUG [aioimaplib:295] Received : b'GJDH4 OK Idle completed.\r\n'
2018-01-18 14:23:21,675 DEBUG [aioimaplib:581] tagged status GJDH4 OK Idle completed.
2018-01-18 14:23:21,675 DEBUG [aioimaplib:360] Sending : b'GJDH5 IDLE\r\n'
2018-01-18 14:23:21,710 DEBUG [aioimaplib:295] Received : b'+ idling\r\n'
2018-01-18 14:23:21,711 DEBUG [aioimaplib:610] continuation line appended to pending sync command GJDH5 IDLE : + idling
['+ idling']
2018-01-18 14:23:21,711 DEBUG [aioimaplib:360] Sending : b'DONE\r\n'
2018-01-18 14:23:21,746 DEBUG [aioimaplib:295] Received : b'GJDH5 OK Idle completed.\r\n'
2018-01-18 14:23:21,747 DEBUG [aioimaplib:581] tagged status GJDH5 OK Idle completed.
2018-01-18 14:23:21,747 DEBUG [aioimaplib:360] Sending : b'GJDH6 IDLE\r\n'
2018-01-18 14:23:21,783 DEBUG [aioimaplib:295] Received : b'+ idling\r\n'
2018-01-18 14:23:21,783 DEBUG [aioimaplib:610] continuation line appended to pending sync command GJDH6 IDLE : + idling
stop_wait_server_push
[…]
I don’t understand why I have this line ['+ idling']
I’m using dovecot as the imap server.
Importing aioimaplib
fails on Python 3.7 because async
is a keyword now.
>>> import aioimaplib
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.7/site-packages/aioimaplib/__init__.py", line 18, in <module>
from .aioimaplib import *
File "/usr/local/lib/python3.7/site-packages/aioimaplib/aioimaplib.py", line 51
'CAPABILITY': Cmd('CAPABILITY', (NONAUTH, AUTH, SELECTED), Exec.async),
^
SyntaxError: invalid syntax
Dear @bamthomas,
In first, I wish you a Happy New Year!
Can you add supports of:
You can add too:
"When using the SASL SCRAM mechanism, the SCRAM-SHA-256-PLUS variant SHOULD be preferred over the SCRAM-SHA-256 variant, and SHA-256 variants [RFC7677] SHOULD be preferred over SHA-1 variants [RFC5802]".
SCRAM-SHA-1(-PLUS):
-- https://tools.ietf.org/html/rfc5802
-- https://tools.ietf.org/html/rfc6120
SCRAM-SHA-256(-PLUS):
-- https://tools.ietf.org/html/rfc7677 since 2015-11-02
-- https://tools.ietf.org/html/rfc8600 since 2019-06-21: https://mailarchive.ietf.org/arch/msg/ietf-announce/suJMmeMhuAOmGn_PJYgX5Vm8lNA
SCRAM-SHA-512(-PLUS):
-- https://tools.ietf.org/html/draft-melnikov-scram-sha-512
SCRAM-SHA3-512(-PLUS):
-- https://tools.ietf.org/html/draft-melnikov-scram-sha3-512
https://xmpp.org/extensions/inbox/hash-recommendations.html
-PLUS variants:
IMAP:
LDAP:
HTTP:
2FA:
IANA:
Linked to:
It seems that not all commands are consistently using the configured timeout, in particular I noticed that for UID
only the UID FETCH
command gets passed the timeout. Is there a reason for this? (I'm not super familiar with IMAP.)
As a workaround I apply await asyncio.wait_for(..., timeout)
manually around the corresponding invocations. However, I noticed that this seems to fail to properly cancel the task. At least in the end I'm left with warnings like this one if timeout occur:
Task was destroyed but it is pending!
task: <Task pending coro=<Command.wait() done, defined at /Users/jgosmann/Library/Caches/pypoetry/virtualenvs/dmarc-metrics-exporter-n4MZO14v-py3.7/lib/python3.7/site-packages/aioimaplib/aioimaplib.py:209> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x103fbb510>()]>>
Asynctest doesn't work with Python 3.8 and above.
Asynctest upstream is aware of this breakage and recommends using the built-in AsyncMock instead.
See also Martiusweb/asynctest#144 and Martiusweb/asynctest#126
Thanks
Thanks for this library - I've had some good success with it so far.
However, if I have an email with a double quote in the subject, then the FETCH
response for the message is split over multiple lines, and uses a literal line for the subject.
Is there a recommended way to handle this? If I do a FETCH
for all messages, then the situation seems even trickier to handle, as there seems to be no easy way to tell which line belongs to which message.
The standard lib imaplib
seems to bundle the responses into tuples of envelopes and data but we only get the response lines (unless I've missed something).
mainly getting errors due to python 3.10 no using a loop anymore, sorry not able to provide more details atm
Hello
first thanks for this library, as it seems to be the only one for imap and asyncio :)
I'm pretty noob to the IMAP protocol and what i find missing is a bit of examples in the README.md on how to do some basic tasks (like actually receiving an email)
for example the IDLE example does show me that I've "received something" but I've no clue how to treat it :)
having some more examples in the readme would compensate not having a formal documentation
thanks again
STOP_WAIT_SERVER_PUSH
is str, but all other Response
s retured from fetch_messages_headers
are bytes.
I am currently stuck with this issue and since you've already merged the fix, all it needs for my local package to update would be a new release. Would it be possible for you to bump to 1.0.1 please?
Would it be possible to rearrange the exceptions so they all share a base class? This could make it simpler to catch all aioimaplib related exceptions.
I am trying to do this with an IDLE loop, in order to reconnect if the connection is lost.
After downloading aioimaplib 1.0.0 I get
============================= test session starts ==============================
platform linux -- Python 3.10.5, pytest-7.1.1, pluggy-1.0.0
rootdir: /build/source
collected 153 items
aioimaplib/tests/test_acceptance_aioimaplib.py . [ 0%]
aioimaplib/tests/test_aioimaplib.py .................................... [ 24%]
.........F........................................ [ 56%]
aioimaplib/tests/test_imapserver.py .............. [ 66%]
aioimaplib/tests/test_imapserver_aioimaplib.py .. [ 67%]
aioimaplib/tests/test_imapserver_imaplib.py ............................ [ 85%]
....... [ 90%]
aioimaplib/tests/test_imapserver_imaplib2.py .. [ 91%]
aioimaplib/tests/test_ssl_cert.py .... [ 94%]
aioimaplib/tests/test_utils.py ......... [100%]
=================================== FAILURES ===================================
______________________ TestAioimaplib.test_get_quotaroot _______________________
self = <aioimaplib.tests.test_aioimaplib.TestAioimaplib testMethod=test_get_quotaroot>
async def test_get_quotaroot(self):
imap_client = await self.login_user('user', 'pass')
self.imapserver.receive(Mail.create(['user']))
response = await imap_client.getquotaroot('INBOX')
> self.assertEqual(response.lines, [b'INBOX (STORAGE 292 5000)', b'GETQUOTAROOT completed.'])
E AssertionError: Lists differ: [b'INBOX (STORAGE 294 5000)', b'GETQUOTAROOT completed.'] != [b'INBOX (STORAGE 292 5000)', b'GETQUOTAROOT completed.']
E
E First differing element 0:
E b'INBOX (STORAGE 294 5000)'
E b'INBOX (STORAGE 292 5000)'
E
E - [b'INBOX (STORAGE 294 5000)', b'GETQUOTAROOT completed.']
E ? ^
E
E + [b'INBOX (STORAGE 292 5000)', b'GETQUOTAROOT completed.']
E ? ^
aioimaplib/tests/test_aioimaplib.py:734: AssertionError
Hi,
so not really a issue but just can't figure it out how to capture this exceptions on aioimaplib? Am I doing something from? Check stackoverflow at https://stackoverflow.com/questions/50668191/python-aioimaplib-catch-exceptions
Counting matching parens in FetchCommand.wait_data() may not be good idea.
fix is coming ...
For the user convenience we decided at the beginning to convert all command response lines to type string.
The problem is that for binary data (attachments...) it is not possible. As a result, Response.lines
can have both str
and bytes
content.
It is causing functions to accept both string and bytes, and to bring unnecessary complexity.
So we should always use bytes.
I want fetch email with this parts '(UID FLAGS INTERNALDATE)'
status, data = await self.imap_client.fetch('3', '(UID FLAGS INTERNALDATE)')
I get wrong response Response(result='OK', lines=['64', 'Success'])
I think this heppens because of
fetch_message_with_literal_data_re = re.compile(rb'\* [0-9]+ FETCH [\w \\\[\]\(\)]+ \{(?P<size>\d+)\}\r\n')
I don't have size field in my response
b'* 64 FETCH (UID 328 INTERNALDATE "14-Oct-2012 17:49:28 +0000" FLAGS (\\Seen))\r\nCGOJ66 OK Success\r\n'
Error in line 611 return (yield from self.protocol.fetch(message_set, message_parts, self.timeout))
Third param is by_uid
. timeout is forth param
Nice library! I'm trying to use it to fetch some GMail messages like so:
res, data = await client.fetch('1:*', '(BODY[HEADER.FIELDS (SUBJECT FROM)])')
This fails with:
AttributeError: 'NoneType' object has no attribute 'append_to_resp'
At this line:
aioimaplib/aioimaplib/aioimaplib.py
Line 449 in 9f4355b
Hi there! 👋
I need help, but I'll probably be vague, and I know this is probably not the best place to ask this. I'm already using the package, so I'll give it a try here. 😅
I'm having an issue when appending a message on the outlook.office365.com
IMAP server. What happened is that it basically keeps on the appending command for a long time (I've removed the timeout, to see if it was just taking too long, but it doesn't finish).
The snippet I'm using is the following:
await imap_client.store(uid, "+FLAGS", "(\\Deleted)")
await imap_client.expunge()
await imap_client.append(message.as_bytes())
I'm trying to modify a body of a message, so I need to get the message, modify it, remove it from the server and add a new one. The log of the first two commands above are fine:
Response(result='OK', lines=[b'4 FETCH (FLAGS (\\Deleted \\Recent))', b'STORE completed.'])
Response(result='OK', lines=[b'4 EXPUNGE', b'EXPUNGE completed.'])
But again, the append keeps forever. 😓
I hope someone can help me out. :)
Thanks for the package!
This can happen as a response to pretty much any command, especially when using XOAUTH for authentication. For example, on MS Exchange Online:
myprefix LIST "" "*"
* BYE Session invalidated - AccessTokenExpired
Ideally it's handled in a way so the user's code can detect this and e.g. reconnect with a new token.
Reported at the Home Assistant project:
2022-07-06 11:27:01 ERROR (MainThread) [homeassistant] Error doing job: Task exception was never retrieved
Traceback (most recent call last):
File "/usr/local/lib/python3.10/asyncio/base_events.py", line 1089, in create_connection
transport, protocol = await self._create_connection_transport(
File "/usr/local/lib/python3.10/asyncio/base_events.py", line 1119, in _create_connection_transport
await waiter
File "/usr/local/lib/python3.10/asyncio/sslproto.py", line 637, in _on_handshake_complete
raise handshake_exc
File "/usr/local/lib/python3.10/asyncio/sslproto.py", line 682, in _process_write_backlog
ssldata = self._sslpipe.do_handshake(
File "/usr/local/lib/python3.10/asyncio/sslproto.py", line 116, in do_handshake
self._sslobj = self._context.wrap_bio(
File "/usr/local/lib/python3.10/ssl.py", line 527, in wrap_bio
return self.sslobject_class._create(
File "/usr/local/lib/python3.10/ssl.py", line 866, in _create
sslobj = context._wrap_bio(
ssl.SSLError: Cannot create a client socket with a PROTOCOL_TLS_SERVER context (_ssl.c:801)
No error when I disable imap sensor in config and restart
This seems to be related to more strict handling in Python 3.10 (which the Home Assistant project has been switched to).
It happens when setting the default SSL context:
aioimaplib/aioimaplib/aioimaplib.py
Line 834 in 544e735
According to documentation, however, this is used to create a server side sockets:
ref: https://docs.python.org/3/library/ssl.html#ssl.Purpose.CLIENT_AUTH
ref: https://docs.python.org/3/library/ssl.html#ssl.Purpose.SERVER_AUTH
Downstream issue: home-assistant/core#74487
举个例子,假设正常ID命令内容为ID ("name" "testname" "version" "1.1.0" "vendor" "abcd") ,使用id方法后的内容为ID ( "name" "testname" "version" "1.1.0" "vendor" "abcd" ) ,在“(”后与“)”前各多了一个空格,导致命令错误
It would be nice if it worked on modern Python versions as well :)
Task exception was never retrieved
future: <Task finished name='Task-2' coro=<BaseEventLoop.create_connection() done, defined at /home/adrian/.pyenv/versions/3.10.5/lib/python3.10/asyncio/base_events.py:972> exception=ConnectionResetError() created at /home/adrian/dev/imapwatch/.venv/lib/python3.10/site-packages/aioimaplib/aioimaplib.py:688>
source_traceback: Object created at (most recent call last):
File "/home/adrian/dev/imapwatch/./imapwatch.py", line 131, in <module>
main()
File "/home/adrian/dev/imapwatch/.venv/lib/python3.10/site-packages/click/core.py", line 1130, in __call__
return self.main(*args, **kwargs)
File "/home/adrian/dev/imapwatch/.venv/lib/python3.10/site-packages/click/core.py", line 1055, in main
rv = self.invoke(ctx)
File "/home/adrian/dev/imapwatch/.venv/lib/python3.10/site-packages/click/core.py", line 1404, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/home/adrian/dev/imapwatch/.venv/lib/python3.10/site-packages/click/core.py", line 760, in invoke
return __callback(*args, **kwargs)
File "/home/adrian/dev/imapwatch/./imapwatch.py", line 127, in main
asyncio.run(async_main(mailbox, host, user, password, test_mode, include_seen), debug=True)
File "/home/adrian/.pyenv/versions/3.10.5/lib/python3.10/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "/home/adrian/.pyenv/versions/3.10.5/lib/python3.10/asyncio/base_events.py", line 633, in run_until_complete
self.run_forever()
File "/home/adrian/.pyenv/versions/3.10.5/lib/python3.10/asyncio/base_events.py", line 600, in run_forever
self._run_once()
File "/home/adrian/.pyenv/versions/3.10.5/lib/python3.10/asyncio/base_events.py", line 1888, in _run_once
handle._run()
File "/home/adrian/.pyenv/versions/3.10.5/lib/python3.10/asyncio/events.py", line 80, in _run
self._context.run(self._callback, *self._args)
File "/home/adrian/dev/imapwatch/./imapwatch.py", line 111, in async_main
await idle_loop(mailbox, host, user, password, test_mode, include_seen)
File "/home/adrian/dev/imapwatch/./imapwatch.py", line 75, in idle_loop
imap_client = IMAP4_SSL(host=host, timeout=30)
File "/home/adrian/dev/imapwatch/.venv/lib/python3.10/site-packages/aioimaplib/aioimaplib.py", line 829, in __init__
super().__init__(host, port, loop, timeout, None, ssl_context)
File "/home/adrian/dev/imapwatch/.venv/lib/python3.10/site-packages/aioimaplib/aioimaplib.py", line 682, in __init__
self.create_client(host, port, loop, conn_lost_cb, ssl_context)
File "/home/adrian/dev/imapwatch/.venv/lib/python3.10/site-packages/aioimaplib/aioimaplib.py", line 835, in create_client
super().create_client(host, port, loop, conn_lost_cb, ssl_context)
File "/home/adrian/dev/imapwatch/.venv/lib/python3.10/site-packages/aioimaplib/aioimaplib.py", line 688, in create_client
local_loop.create_task(local_loop.create_connection(lambda: self.protocol, host, port, ssl=ssl_context))
Traceback (most recent call last):
File "/home/adrian/.pyenv/versions/3.10.5/lib/python3.10/asyncio/base_events.py", line 1089, in create_connection
transport, protocol = await self._create_connection_transport(
File "/home/adrian/.pyenv/versions/3.10.5/lib/python3.10/asyncio/base_events.py", line 1119, in _create_connection_transport
await waiter
ConnectionResetError
# creates IMAP4 instace, ImapClientProtocol and task for connection
imap = IMAP4(host, port)
# waits for successful connection (state changes from AUTH to NONAUTH)
await imap.wait_hello_from_server()
# changes state to AUTH
await imap.login(username, password) # or imap.authenticate
# creates IMAP4 instace, does not start connection
imap = IMAP4(loop=myloop, timeout=10)
# creates ImapClientProtocol
# connects to (await loop.create_connection)
await imap.connect(host, port)
# changes state to AUTH
await imap.login(username, password) # or imap.authenticate
connect
is more readable and intuitive than wait_hello_from_server
# creates class, does not start connection
imap = IMAP4(host, port)
# creates ImapClientProtocol
# connection (await loop.create_connection)
# changes state to AUTH
await imap.login(username, password) # or imap.authenticate
What do you think about these updates?
Hello,
I would have liked to use the "imapserver.py" script in a unit test.
Being a commercial application, I preferred not to embed GPL code. Would it be possible to have a less restrictive license for this file?
Thank you very much for your work.
Alexandre
Hi,
I think that user should be responsible to check for capabilities supported by IMAP server and use only them.
Library should not restrict user this way.
Hi! I want to connect to server using proxy.
How use proxies with aioimaplib?
For example:
IP:PORT:LOGIN:PASSWORD
Currently running the example with python 3.9.4 errors are thrown (presumably due to changes in asyncio):
Task exception was never retrieved
future: <Task finished name='Task-4' coro=<IMAP4ClientProtocol.welcome() done, defined at /home/james/dls/aioimaplib/aioimaplib/aioimaplib.py:298> exception=TypeError("object Condition can't be used in 'await' expression")>
Traceback (most recent call last):
File "/home/james/dls/aioimaplib/aioimaplib/aioimaplib.py", line 300, in wrapper
with await self.state_condition:
TypeError: object Condition can't be used in 'await' expression
Traceback (most recent call last):
I intend to look into this, although my experience with async is limited.
When I run the test suite (python setup.py test
) on a 64-bit ARM machine running NixOS and Python 3.6, I get the following error:
======================================================================
FAIL: test_idle_start__exits_queueget_without_timeout_error (aioimaplib.tests.test_aioimaplib.TestAioimaplibClocked)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/nix/store/q4mxm1959nr9bh6vmbml6f5vr0phfgh8-python3.6-asynctest-0.11.1/lib/python3.6/site-packages/asynctest/case.py", line 294, in run
self._run_test_method(testMethod)
File "/nix/store/q4mxm1959nr9bh6vmbml6f5vr0phfgh8-python3.6-asynctest-0.11.1/lib/python3.6/site-packages/asynctest/case.py", line 351, in _run_test_method
self.loop.run_until_complete(result)
File "/nix/store/q4mxm1959nr9bh6vmbml6f5vr0phfgh8-python3.6-asynctest-0.11.1/lib/python3.6/site-packages/asynctest/case.py", line 221, in wrapper
return method(*args, **kwargs)
File "/nix/store/4yfkb61l14x37pkqdmg4ck61xh5wg49z-python3-3.6.4/lib/python3.6/asyncio/base_events.py", line 467, in run_until_complete
return future.result()
File "/build/source/aioimaplib/tests/test_aioimaplib.py", line 852, in test_idle_start__exits_queueget_without_timeout_error
self.assertEqual(STOP_WAIT_SERVER_PUSH, r)
AssertionError: 'stop_wait_server_push' != ['OK Still here', 'OK Still here', 'OK St[1263 chars]ere']
-------------------- >> begin captured logging << --------------------
asyncio: DEBUG: Using selector: EpollSelector
--------------------- >> end captured logging << ---------------------
Do you know the reason for this?
Using 0.8.0:
Traceback (most recent call last):
File "/app/slack_responding/test.py", line 26, in <module>
loop.run_until_complete(
File "/usr/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
return future.result()
File "/app/slack_responding/test.py", line 8, in wait_for_new_message
await imap_client.wait_hello_from_server()
File "/app/.venv/lib/python3.9/site-packages/aioimaplib/aioimaplib.py", line 694, in wait_hello_from_server
await asyncio.wait_for(self.protocol.wait('AUTH|NONAUTH'), self.timeout)
File "/usr/lib/python3.9/asyncio/tasks.py", line 481, in wait_for
return fut.result()
File "/app/.venv/lib/python3.9/site-packages/aioimaplib/aioimaplib.py", line 594, in wait
with await self.state_condition:
TypeError: object Condition can't be used in 'await' expression
I just pulled the newest version of this library and I'm still getting the following error when using Python 3.9
Task exception was never retrieved
future: <Task finished name='Task-2' coro=<BaseEventLoop.create_connection() done, defined at /usr/lib/python3.9/asyncio/base_events.py:967> exception=gaierror(-2, 'Name or service not known')>
This is the same as already mentioned in #59 (comment).
To reproduce this, I simply ran the imap_fetch.py
example provided.
Hey,
I am new to asynchronous python, I read your code & found you were using async.coroutine & yield from. I didn't know what are those decorators & keywords because I started coding in Python after 3.6.
I would like to suggest an enhancement to change async.coroutine & yield from to async/await keywords to improve readability for newer users & it'll modernize your code base.
Edit:
Besides all that, thanks a lot for working on this project, It is the only library I could find with proper implementation of imap with async.
Thanks :)
Hi, i have some troubles with APPEND command.
It receives message that server ready to receive literal data and do nothing.
This is part of asyncio debug log:
DEBUG:aioimaplib.aioimaplib:Sending : b'LION1 LOGIN xxx "xxx"\r\n'
DEBUG:asyncio:poll 59997.714 ms took 57.081 ms: 1 events
DEBUG:aioimaplib.aioimaplib:Received : b'* CAPABILITY IMAP4rev1 CHILDREN UNSELECT LITERAL+ NAMESPACE XLIST BINARY UIDPLUS ENABLE ID IDLE MOVE\r\nLION1 OK LOGIN Completed.\r\n'
DEBUG:aioimaplib.aioimaplib:tagged status LION1 OK LOGIN Completed.
DEBUG:aioimaplib.aioimaplib:state -> AUTH
DEBUG:aioimaplib.aioimaplib:Sending : b'LION2 APPEND INBOX {17602}\r\n'
DEBUG:asyncio:poll 59998.721 ms took 9.665 ms: 1 events
DEBUG:aioimaplib.aioimaplib:Received : b'+ go ahead with 17602 octets\r\n'
DEBUG:aioimaplib.aioimaplib:continuation line appended to pending sync command LION2 APPEND INBOX {17602} : + go ahead with 17602 octets
INFO:asyncio:poll 59987.636 ms took 59991.798 ms: timeout
There seems to be in bug in the aioimaplib library, it expects the “IMAPrev1” to be the first capability (index 0):
aioimaplib/aioimaplib/aioimaplib.py
Line 558 in 9670d43
However, for example imap.telnet.be returns capabilities in roughly alphabetical order:
$ telnet imap.telenet.be 143
Trying 2a02:1800:100:3::3...
Connected to imap.telenet.be.
Escape character is '^]'.
which seems to be acceptable by the IMAP RFC 3501:
The server MUST send a single untagged CAPABILITY response with “IMAP4rev1” as one of the listed capabilities before the (tagged) OK response.
Since updating from Python 3.6.4 to 3.6.5, I'm getting test failures:
======================================================================
FAIL: test_append (aioimaplib.tests.test_aioimaplib.TestAioimaplib)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/nix/store/h58bwyphmcawijhx0387af6bvnk9ggcb-python3.6-asynctest-0.12.0/lib/python3.6/site-packages/asynctest/case.py", line 297, in run
self._run_test_method(testMethod)
File "/nix/store/h58bwyphmcawijhx0387af6bvnk9ggcb-python3.6-asynctest-0.12.0/lib/python3.6/site-packages/asynctest/case.py", line 354, in _run_test_method
self.loop.run_until_complete(result)
File "/nix/store/h58bwyphmcawijhx0387af6bvnk9ggcb-python3.6-asynctest-0.12.0/lib/python3.6/site-packages/asynctest/case.py", line 224, in wrapper
return method(*args, **kwargs)
File "/nix/store/33v7rg3qmwfj3smvhna5634n7x286wl6-python3-3.6.5/lib/python3.6/asyncio/base_events.py", line 468, in run_until_complete
return future.result()
File "/build/source/aioimaplib/tests/test_aioimaplib.py", line 724, in test_append
self.assertEquals('OK', response.result)
AssertionError: 'OK' != 'BAD'
- OK
+ BAD
-------------------- >> begin captured logging << --------------------
asyncio: DEBUG: Using selector: EpollSelector
--------------------- >> end captured logging << ---------------------
======================================================================
FAIL: test_idle_start__exits_queueget_without_timeout_error (aioimaplib.tests.test_aioimaplib.TestAioimaplibClocked)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/nix/store/h58bwyphmcawijhx0387af6bvnk9ggcb-python3.6-asynctest-0.12.0/lib/python3.6/site-packages/asynctest/case.py", line 297, in run
self._run_test_method(testMethod)
File "/nix/store/h58bwyphmcawijhx0387af6bvnk9ggcb-python3.6-asynctest-0.12.0/lib/python3.6/site-packages/asynctest/case.py", line 354, in _run_test_method
self.loop.run_until_complete(result)
File "/nix/store/h58bwyphmcawijhx0387af6bvnk9ggcb-python3.6-asynctest-0.12.0/lib/python3.6/site-packages/asynctest/case.py", line 224, in wrapper
return method(*args, **kwargs)
File "/nix/store/33v7rg3qmwfj3smvhna5634n7x286wl6-python3-3.6.5/lib/python3.6/asyncio/base_events.py", line 468, in run_until_complete
return future.result()
File "/build/source/aioimaplib/tests/test_aioimaplib.py", line 872, in test_idle_start__exits_queueget_without_timeout_error
self.assertEqual(STOP_WAIT_SERVER_PUSH, r)
AssertionError: 'stop_wait_server_push' != ['OK Still here']
-------------------- >> begin captured logging << --------------------
asyncio: DEBUG: Using selector: EpollSelector
--------------------- >> end captured logging << ---------------------
Sorry, this isnt really an issue but could not find any where to ask this. How fast is aiomaplib compared to the standard imaplib? How fast would aioimaplib be able to scrape (get the content of) lets say, 100,000 emails?
The example in the README.rs
file does this:
res, data = await imap_client.select()
print('there is %s messages INBOX' % data[0])
which would be the same behaviour of imaplib
. But when I execute this code I get b'FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft \\Forwarded $Junk $la'
instead of the number of emails. I get a list that looks like this:
[b'FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft \\Forwarded $Junk $la'
b'bel4 $label5 $label2 $label3 $label1 Junk $Forwarded $MDNSent NonJunk)',
b'OK [PERMANENTFLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft \\Forwar'
b'ded $Junk $label4 $label5 $label2 $label3 $label1 Junk $Forwarded $MDNSent N'
b'onJunk \\*)]',
b'OK [URLMECH INTERNAL]',
b'4046 EXISTS',
b'0 RECENT',
b'OK [UNSEEN 1507]',
b'OK [UIDVALIDITY 1226754362]',
b'OK [UIDNEXT 127124]',
b'[READ-WRITE] SELECT completed']
If I execute a select()
with the python built-in imaplib
with the same credentials, then I get expected behaviour. I checked different imap servers and this always happens. Why is that?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.