3

I'm trying to delete all email messages in a folder outside of my account.inbox with Python 3 and exchangelib.

testFolder = account.root / 'Top of Information Store' / 'Test'    
emails = testFolder.all().order_by('-datetime_received')
for email in emails:
    email.delete()
    # FWIW
    #print(email.subject)
    #email.move(to_another_folder)
    #both work fine.

The above only prints all emails and their properties

I have also tried:

items=[]
testFolder = account.root / 'Top of Information Store' / 'Test'    
emails = testFolder.all().order_by('-datetime_received')
for email in emails:
    items.append(Message(folder=testFolder))
items.save()
items.delete()

The above hangs and gives a memory error:

File "C:\Python36-32\lib\site-packages\exchangelib\services.py", line 89, in _
get_elements
    response = self._get_response_xml(payload=payload)
  File "C:\Python36-32\lib\site-packages\exchangelib\services.py", line 171, in
_get_response_xml
    res = self._get_soap_payload(response=r, **parse_opts)
  File "C:\Python36-32\lib\site-packages\exchangelib\services.py", line 260, in
_get_soap_payload
    root = to_xml(response.iter_content())
  File "C:\Python36-32\lib\site-packages\exchangelib\util.py", line 365, in to_x
ml
    return parse(stream, parser=forgiving_parser)
  File "C:\Python36-32\lib\site-packages\defusedxml\lxml.py", line 134, in parse
  elementtree = _etree.parse(source, parser, base_url=base_url)
  File "src\lxml\etree.pyx", line 3424, in lxml.etree.parse
  File "src\lxml\parser.pxi", line 1857, in lxml.etree._parseDocument
  File "C:\Python36-32\lib\site-packages\exchangelib\util.py", line 340, in getv
alue
    res = b''.join(self._bytes_generator)
MemoryError

I'm not sure what else I should try, especially since the former works to move and print emails just fine.

2 Answers2

2

There's no need to collect the items first. This will consume a lot of memory, as you found out. Instead, you can call .delete() directly on the QuerySet. This will fetch only the item IDs and delete them in batches.

There is also an .empty() method which results in a call to the EmptyFolder service. This will delete all items in the folder without even fetching the IDs:

testFolder.all().delete()
# Or even faster:
testFolder.empty()
Erik Cederstrand
  • 9,643
  • 8
  • 39
  • 63
  • Awesome! .empty() works perfectly! I hate to ask a question on an answer, but is there a way to do a .move() on a QuerySet? – StrangerDanger Jan 30 '19 at 14:48
  • 1
    There's no `.move()` on a queryset, but you can feed a queryset to `account.bulk_move()`. – Erik Cederstrand Jan 30 '19 at 15:12
  • I've tried going that route...but i'm doing it wrong. `qs = testFolder.all()` then `account.bulk_move(qs=, to_folder=newTestFolder)` and several other variations with no success, still receiving a memory error. This should probably be a new question... – StrangerDanger Jan 30 '19 at 15:44
  • 1
    You don't need the full item content, just the IDs:. This should reduce the memory pressure: `account.bulk_move(qs= testFolder.all().only('id', 'changekey'), to_folder=newTestFolder) – Erik Cederstrand Jan 30 '19 at 19:09
  • You are super helpful! Thank you for your help and for developing this awesome package! This will save my team 200 work hours a month! – StrangerDanger Jan 30 '19 at 23:40
  • Thanks. I'm glad you found me and my software helpful! – Erik Cederstrand Feb 02 '19 at 11:23
0

Maybe instead of delete try move to trash? or soft delete?

Aiden Zhao
  • 633
  • 4
  • 15