Skip to content

Commit

Permalink
MailBox.fetch - added "reverse" parameter,
Browse files Browse the repository at this point in the history
in utils.parse_email_address used email, .utils.parseaddr,
added tests for message attributes
  • Loading branch information
ikvk committed Jan 28, 2020
1 parent 15a2157 commit eb939fd
Show file tree
Hide file tree
Showing 17 changed files with 1,275 additions and 24 deletions.
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ MailBox.fetch - email message generator, first searches email ids by criteria, t
* *miss_defect*: True, miss emails with defects
* *miss_no_uid*: True, miss emails without uid
* *mark_seen*: True, mark emails as seen on fetch
* *reverse*: False, in order from the larger date to the smaller

Email attributes
^^^^^^^^^^^^^^^^
Expand Down
7 changes: 6 additions & 1 deletion examples/_notes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,9 @@ False False _payload (*)

* - some other encoding is used, or the header is missing,
or if the payload has bogus data (i.e. bogus base64 or uuencoded data),
the payload is returned as-is.
the payload is returned as-is.


icons
=====
📨 📬 📪 📭 📫 ✉ 📧 🖂 🖃 🖅 📩
8 changes: 8 additions & 0 deletions examples/msg_from_file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from imap_tools import MailMessage

with open('.../attachment_2_base64.eml', 'rb') as f:
bytes_data = f.read()
msg = MailMessage.from_bytes(bytes_data)
print(msg.date_str, msg.subject)
for i, att in msg.attachments:
print('-', att.filename, att.content_type, len(att.payload))
6 changes: 4 additions & 2 deletions imap_tools/mailbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def _criteria_encoder(criteria: str or bytes, charset: str) -> str or bytes:
return criteria if type(criteria) is bytes else str(criteria).encode(charset)

def fetch(self, criteria: str or bytes = 'ALL', charset: str = 'US-ASCII', limit: int = None,
miss_defect=True, miss_no_uid=True, mark_seen=True) -> iter:
miss_defect=True, miss_no_uid=True, mark_seen=True, reverse=False) -> iter:
"""
Mail message generator in current folder by search criteria
:param criteria: message search criteria (see examples at ./doc/imap_search_criteria.txt)
Expand All @@ -85,12 +85,14 @@ def fetch(self, criteria: str or bytes = 'ALL', charset: str = 'US-ASCII', limit
:param miss_defect: miss emails with defects
:param miss_no_uid: miss emails without uid
:param mark_seen: mark emails as seen on fetch
:param reverse: in order from the larger date to the smaller
:return generator: MailMessage
"""
search_result = self.box.search(charset, self._criteria_encoder(criteria, charset))
check_command_status('box.search', search_result)
message_id_set = search_result[1][0].decode().split(' ') if search_result[1][0] else ()
# first element is string with email numbers through the gap
for i, message_id in enumerate(search_result[1][0].decode().split(' ') if search_result[1][0] else ()):
for i, message_id in enumerate((reversed if reverse else iter)(message_id_set)):
if limit and i >= limit:
break
# get message by id
Expand Down
2 changes: 1 addition & 1 deletion imap_tools/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def _parse_addresses(self, value: str) -> (dict,):
if value not in self.obj:
return ()
return tuple(
parse_email_address(''.join(decode_value(string, charset) for string, charset, in decode_header(address)))
parse_email_address(''.join(decode_value(string, charset) for string, charset in decode_header(address)))
for address in self.obj[value].split(',')
)

Expand Down
17 changes: 7 additions & 10 deletions imap_tools/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import re
import inspect
import datetime
from email.utils import parseaddr

short_month_names = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', "Dec")

Expand Down Expand Up @@ -63,16 +64,12 @@ def parse_email_address(value: str) -> dict:
@:return dict(name: str, email: str, full: str)
"""
address = ''.join(char for char in value if char.isprintable()).strip()
address = re.sub('[\n\r\t]+', ' ', address)
result = {'email': '', 'name': '', 'full': address}
match = re.match('(?P<name>.*)?<(?P<email>.*@.*)>', address, re.UNICODE)
if match:
group = match.groupdict()
result['name'] = group['name'].strip()
result['email'] = group['email'].strip()
else:
result['email' if '@' in address else 'name'] = address
return result
parsed = parseaddr(address)
return {
'email': parsed[1] if '@' in parsed[1] else '',
'name': parsed[0],
'full': address
}


def parse_email_date(value: str) -> datetime.datetime:
Expand Down
6 changes: 6 additions & 0 deletions release_notes.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
0.12.0
======
* MailBox.fetch - added "reverse" parameter
* in utils.parse_email_address used email.utils.parseaddr
* added tests for message attributes

0.11.1
======
* message.Attachment.payload - removed probability of return None
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@

setuptools.setup(
name='imap_tools',
version='0.11.1',
version='0.12.0',
packages=setuptools.find_packages(),
url='https://github.com/ikvk/imap_tools',
license='Apache-2.0',
long_description=long_description,
long_description_content_type="text/x-rst",
author='v.kaukin',
author_email='workkvk@gmail.com',
author_email='KaukinVK@ya.com',
description='Working with email and mailbox using IMAP protocol.',
keywords=['imap', 'imap-client', 'python3', 'email'],
classifiers=[
Expand Down
375 changes: 375 additions & 0 deletions tests/data.py

Large diffs are not rendered by default.

Loading

0 comments on commit eb939fd

Please sign in to comment.