2

I've build a very simple script that fetches emails from an Outlook inbox. It looks like this:

from imap_tools import MailBox

inbox = MailBox("outlook.office365.com").login("email", "password")

for email in inbox.fetch():  # bulk=True
    print(email)

It's working when I use it like this. Though, when passing the arg bulk=True on the fetch() function it will raise the following error:

Traceback (most recent call last):
  File "/Users/julius/Library/Application Support/JetBrains/IntelliJIdea2022.3/scratches/scratch_1.py", line 5, in <module>
    for email in inbox.fetch(bulk=True):
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/imap_tools/mailbox.py", line 171, in fetch
    for fetch_item in (self._fetch_in_bulk if bulk else self._fetch_by_one)(nums, message_parts, reverse):  # noqa
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/imap_tools/mailbox.py", line 144, in _fetch_in_bulk
    fetch_result = self.client.fetch(','.join(message_nums), message_parts)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/imaplib.py", line 548, in fetch
    typ, dat = self._simple_command(name, message_set, message_parts)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/imaplib.py", line 1230, in _simple_command
    return self._command_complete(name, self._command(name, *args))
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/imaplib.py", line 1008, in _command
    raise self.abort('socket error: %s' % val)
imaplib.IMAP4.abort: socket error: EOF occurred in violation of protocol (_ssl.c:2396)

Has anyone got a clue on how to fix this?

Vladimir
  • 6,162
  • 2
  • 32
  • 36
  • Can you get a protocol trace? How many messages are in your inbox? If it's more then a couple dozen, the server is probably refusing to provide that many messages in a single command. The full stack trace would also help. The documents suggest using limit=, but I'm not sure how you would get the rest of the messages then. – Max Dec 12 '22 at 21:41
  • It's about 1000 messages, so what you are saying could very well be correct. Also, I've added the full error traceback in the question. – Julius Jonker Dec 12 '22 at 22:08
  • 2
    It looks like imap_tools.fetch supports a limit parameter that can take a "slice" to provide a range, so you could probably do something like `limit=slice(n,n+100)` in a loop until you get no messages (increasing n each time). The implementation is a bit inefficient by doing a server side search every time rather than allowing you to directly provide the message numbers you want.... And you can tweak the slice size up and down until it doesn't break. – Max Dec 12 '22 at 22:14
  • 2
    Thanks a lot for your help! I didn't realise I could combine the arg `bulk` with `limit`. Knowing I can solves my issue completely. Thankyou :) – Julius Jonker Dec 12 '22 at 22:18
  • @Max, I will add info about limit by slice into docs – Vladimir Dec 15 '22 at 13:06
  • @Vladimir Are you the maintainer? If so, can I suggest a variant of fetch that takes numbers() or uids() directly so the search does not have to re-performed every time? It would be very useful for paging. Like the other functions (store/delete/etc) all take UID ranges. – Max Dec 15 '22 at 14:26
  • @Max, yes, make issue or PR for discuss. I think overhead for UID in fetch is minimal, but I do not make time tests for it. Also there is 2 methods in lib: numbers() and uids(). Page example: https://github.com/ikvk/imap_tools/blob/master/examples/fetch_by_pages.py – Vladimir Dec 16 '22 at 07:11
  • @Vladimir Yep, I am aware of uids(), just surprised there isn't a fetch() method that takes a list of UIDs directly (unless I missed it). – Max Dec 16 '22 at 14:16
  • @Max, search arg "uid" - iter(str)/str/U – Vladimir Dec 19 '22 at 03:36

1 Answers1

1

Right answer: combine bulk and limit args.

Text from docs: For actions with a large number of messages imap command may be too large and will cause exception at server side, use 'limit' argument for fetch in this case.

Vladimir
  • 6,162
  • 2
  • 32
  • 36