Hello community, here is the log from the commit of package python-imbox for openSUSE:Factory checked in at 2020-05-11 13:42:18 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-imbox (Old) and /work/SRC/openSUSE:Factory/.python-imbox.new.2738 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-imbox" Mon May 11 13:42:18 2020 rev:5 rq:802507 version:0.9.7 Changes: -------- --- /work/SRC/openSUSE:Factory/python-imbox/python-imbox.changes 2018-09-03 10:36:34.884898680 +0200 +++ /work/SRC/openSUSE:Factory/.python-imbox.new.2738/python-imbox.changes 2020-05-11 13:42:25.357255565 +0200 @@ -1,0 +2,8 @@ +Sun May 3 13:06:36 UTC 2020 - Sebastian Wagner <sebix+novell....@sebix.at> + +- update to version 0.9.7: + - Gmail: IMAP extension searches label and raw are not supported. + - Searches in mail bodies and UID ranges are now supported. + - Attachments have a Content-ID now (#174) + +------------------------------------------------------------------- Old: ---- imbox-0.9.6.tar.gz New: ---- imbox-0.9.7.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-imbox.spec ++++++ --- /var/tmp/diff_new_pack.TCZ6dI/_old 2020-05-11 13:42:26.509258010 +0200 +++ /var/tmp/diff_new_pack.TCZ6dI/_new 2020-05-11 13:42:26.513258018 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-imbox # -# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2020 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -12,19 +12,19 @@ # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. -# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# Please submit bugfixes or comments via https://bugs.opensuse.org/ # %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-imbox -Version: 0.9.6 +Version: 0.9.7 Release: 0 Summary: Python IMAP for Human beings License: MIT Group: Development/Languages/Python -Url: https://github.com/martinrusev/imbox +URL: https://github.com/martinrusev/imbox Source: https://files.pythonhosted.org/packages/source/i/imbox/imbox-%{version}.tar.gz BuildRequires: %{python_module devel} BuildRequires: %{python_module setuptools} ++++++ imbox-0.9.6.tar.gz -> imbox-0.9.7.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/imbox-0.9.6/CHANGELOG.md new/imbox-0.9.7/CHANGELOG.md --- old/imbox-0.9.6/CHANGELOG.md 2018-08-14 17:23:46.000000000 +0200 +++ new/imbox-0.9.7/CHANGELOG.md 2020-05-03 13:55:23.000000000 +0200 @@ -1,4 +1,4 @@ -## 0.9.6 (16 August 2018) +## 0.9.6 (14 August 2018) IMPROVEMENTS: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/imbox-0.9.6/PKG-INFO new/imbox-0.9.7/PKG-INFO --- old/imbox-0.9.6/PKG-INFO 2018-08-14 17:26:01.000000000 +0200 +++ new/imbox-0.9.7/PKG-INFO 2020-05-03 13:59:43.632998700 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: imbox -Version: 0.9.6 +Version: 0.9.7 Summary: Python IMAP for Human beings Home-page: https://github.com/martinrusev/imbox Author: Martin Rusev @@ -84,9 +84,16 @@ # Messages whose subjects contain a string inbox_messages_subject_christmas = imbox.messages(subject='Christmas') + # Messages whose UID is greater than 1050 + inbox_messages_uids_greater_than_1050 = imbox.messages(uid__range='1050:*') + # Messages from a specific folder messages_in_folder_social = imbox.messages(folder='Social') + # Some of Gmail's IMAP Extensions are supported (label and raw): + all_messages_with_an_attachment_from_martin = imbox.messages(folder='all', raw='from:mar...@amon.cx has:attachment') + all_messages_labeled_finance = imbox.messages(folder='all', label='finance') + for uid, message in all_inbox_messages: # Every message is an object with the following keys diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/imbox-0.9.6/README.rst new/imbox-0.9.7/README.rst --- old/imbox-0.9.6/README.rst 2018-08-14 17:22:47.000000000 +0200 +++ new/imbox-0.9.7/README.rst 2020-05-03 13:55:23.000000000 +0200 @@ -76,9 +76,16 @@ # Messages whose subjects contain a string inbox_messages_subject_christmas = imbox.messages(subject='Christmas') + # Messages whose UID is greater than 1050 + inbox_messages_uids_greater_than_1050 = imbox.messages(uid__range='1050:*') + # Messages from a specific folder messages_in_folder_social = imbox.messages(folder='Social') + # Some of Gmail's IMAP Extensions are supported (label and raw): + all_messages_with_an_attachment_from_martin = imbox.messages(folder='all', raw='from:mar...@amon.cx has:attachment') + all_messages_labeled_finance = imbox.messages(folder='all', label='finance') + for uid, message in all_inbox_messages: # Every message is an object with the following keys diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/imbox-0.9.6/imbox/__init__.py new/imbox-0.9.7/imbox/__init__.py --- old/imbox-0.9.6/imbox/__init__.py 2018-08-14 17:24:05.000000000 +0200 +++ new/imbox-0.9.7/imbox/__init__.py 2020-05-03 13:56:27.000000000 +0200 @@ -1,9 +1,8 @@ from imbox.imbox import Imbox -__version_info__ = (0, 9, 6) +__version_info__ = (0, 9, 7) __version__ = '.'.join([str(x) for x in __version_info__]) __all__ = ['Imbox'] - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/imbox-0.9.6/imbox/imbox.py new/imbox-0.9.7/imbox/imbox.py --- old/imbox-0.9.6/imbox/imbox.py 2018-08-14 17:22:47.000000000 +0200 +++ new/imbox-0.9.7/imbox/imbox.py 2020-05-03 13:55:23.000000000 +0200 @@ -28,14 +28,16 @@ self.vendor = vendor or hostname_vendorname_dict.get(self.hostname) if self.vendor is not None: - self.authentication_error_message = name_authentication_string_dict.get(self.vendor) + self.authentication_error_message = name_authentication_string_dict.get( + self.vendor) try: self.connection = self.server.connect(username, password) except imaplib.IMAP4.error as e: if self.authentication_error_message is None: raise - raise imaplib.IMAP4.error(self.authentication_error_message + '\n' + str(e)) + raise imaplib.IMAP4.error( + self.authentication_error_message + '\n' + str(e)) logger.info("Connected to IMAP Server with user {username} on {hostname}{ssl}".format( hostname=hostname, username=username, ssl=(" over SSL" if ssl or starttls else ""))) @@ -61,16 +63,18 @@ self.connection.uid('STORE', uid, '+FLAGS', '(\\Flagged)') def delete(self, uid): - logger.info("Mark UID {} with \\Deleted FLAG and expunge.".format(int(uid))) - self.connection.uid('STORE', uid, '+FLAGS', '(\\Deleted)') + logger.info( + "Mark UID {} with \\Deleted FLAG and expunge.".format(int(uid))) self.connection.expunge() def copy(self, uid, destination_folder): - logger.info("Copy UID {} to {} folder".format(int(uid), str(destination_folder))) + logger.info("Copy UID {} to {} folder".format( + int(uid), str(destination_folder))) return self.connection.uid('COPY', uid, destination_folder) def move(self, uid, destination_folder): - logger.info("Move UID {} to {} folder".format(int(uid), str(destination_folder))) + logger.info("Move UID {} to {} folder".format( + int(uid), str(destination_folder))) if self.copy(uid, destination_folder): self.delete(uid) @@ -83,8 +87,10 @@ messages_class = GmailMessages if folder: - self.connection.select(messages_class.folder_lookup.get((folder.lower())) or folder) + self.connection.select( + messages_class.FOLDER_LOOKUP.get((folder.lower())) or folder) msg = " from folder '{}'".format(folder) + del kwargs['folder'] else: msg = " from inbox" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/imbox-0.9.6/imbox/messages.py new/imbox-0.9.7/imbox/messages.py --- old/imbox-0.9.6/imbox/messages.py 2018-08-14 17:22:47.000000000 +0200 +++ new/imbox-0.9.7/imbox/messages.py 2020-05-03 13:55:23.000000000 +0200 @@ -1,14 +1,30 @@ -from imbox.parser import fetch_email_by_uid +import datetime +import logging + from imbox.query import build_search_query +from imbox.parser import fetch_email_by_uid -import logging logger = logging.getLogger(__name__) class Messages: - folder_lookup = {} + IMAP_ATTRIBUTE_LOOKUP = { + 'unread': '(UNSEEN)', + 'flagged': '(FLAGGED)', + 'unflagged': '(UNFLAGGED)', + 'sent_from': '(FROM "{}")', + 'sent_to': '(TO "{}")', + 'date__gt': '(SINCE "{}")', + 'date__lt': '(BEFORE "{}")', + 'date__on': '(ON "{}")', + 'subject': '(SUBJECT "{}")', + 'uid__range': '(UID {})', + 'text': '(TEXT "{}")', + } + + FOLDER_LOOKUP = {} def __init__(self, connection, @@ -28,8 +44,8 @@ parser_policy=self.parser_policy) def _query_uids(self, **kwargs): - query_ = build_search_query(**kwargs) - message, data = self.connection.uid('search', None, query_) + query_ = build_search_query(self.IMAP_ATTRIBUTE_LOOKUP, **kwargs) + _, data = self.connection.uid('search', None, query_) if data[0] is None: return [] return data[0].split() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/imbox-0.9.6/imbox/parser.py new/imbox-0.9.7/imbox/parser.py --- old/imbox-0.9.6/imbox/parser.py 2018-08-14 17:22:47.000000000 +0200 +++ new/imbox-0.9.7/imbox/parser.py 2020-05-03 13:55:23.000000000 +0200 @@ -60,7 +60,8 @@ for index, (address_name, address_email) in enumerate(addresses): addresses[index] = {'name': decode_mail_header(address_name), 'email': address_email} - logger.debug("{} Mail address in message: <{}> {}".format(header_name.upper(), address_name, address_email)) + logger.debug("{} Mail address in message: <{}> {}".format( + header_name.upper(), address_name, address_email)) return addresses @@ -100,7 +101,8 @@ attachment = { 'content-type': message_part.get_content_type(), 'size': len(file_data), - 'content': io.BytesIO(file_data) + 'content': io.BytesIO(file_data), + 'content-id': message_part.get("Content-ID", None) } filename = message_part.get_param('name') if filename: @@ -111,7 +113,8 @@ name, value = decode_param(param) if 'file' in name: - attachment['filename'] = value[1:-1] if value.startswith('"') else value + attachment['filename'] = value[1:- + 1] if value.startswith('"') else value if 'create-date' in name: attachment['create-date'] = value @@ -163,9 +166,11 @@ email_parse_kwargs = {} try: - email_message = email.message_from_string(raw_email, **email_parse_kwargs) + email_message = email.message_from_string( + raw_email, **email_parse_kwargs) except UnicodeEncodeError: - email_message = email.message_from_string(raw_email.encode('utf-8'), **email_parse_kwargs) + email_message = email.message_from_string( + raw_email.encode('utf-8'), **email_parse_kwargs) maintype = email_message.get_content_maintype() parsed_email = {'raw_email': raw_email} @@ -187,7 +192,7 @@ content = decode_content(part) is_inline = content_disposition is None \ - or content_disposition.startswith("inline") + or content_disposition.startswith("inline") if content_type == "text/plain" and is_inline: body['plain'].append(content) elif content_type == "text/html" and is_inline: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/imbox-0.9.6/imbox/query.py new/imbox-0.9.7/imbox/query.py --- old/imbox-0.9.6/imbox/query.py 2018-08-14 17:22:47.000000000 +0200 +++ new/imbox-0.9.7/imbox/query.py 2020-05-03 13:55:23.000000000 +0200 @@ -1,61 +1,17 @@ import datetime -import logging -# TODO - Validate query arguments -logger = logging.getLogger(__name__) - - -def format_date(date): - if isinstance(date, datetime.date): - return date.strftime('%d-%b-%Y') - return date - - -def build_search_query(**kwargs): - - # Parse keyword arguments - unread = kwargs.get('unread', False) - unflagged = kwargs.get('unflagged', False) - flagged = kwargs.get('flagged', False) - sent_from = kwargs.get('sent_from', False) - sent_to = kwargs.get('sent_to', False) - date__gt = kwargs.get('date__gt', False) - date__lt = kwargs.get('date__lt', False) - date__on = kwargs.get('date__on', False) - subject = kwargs.get('subject') +def build_search_query(imap_attribute_lookup, **kwargs): query = [] - - if unread: - query.append("(UNSEEN)") - - if unflagged: - query.append("(UNFLAGGED)") - - if flagged: - query.append("(FLAGGED)") - - if sent_from: - query.append('(FROM "%s")' % sent_from) - - if sent_to: - query.append('(TO "%s")' % sent_to) - - if date__gt: - query.append('(SINCE "%s")' % format_date(date__gt)) - - if date__lt: - query.append('(BEFORE "%s")' % format_date(date__lt)) - - if date__on: - query.append('(ON "%s")' % format_date(date__on)) - - if subject is not None: - query.append('(SUBJECT "%s")' % subject) + for name, value in kwargs.items(): + if value is not None: + if isinstance(value, datetime.date): + value = value.strftime('%d-%b-%Y') + if isinstance(value, str) and '"' in value: + value = value.replace('"', "'") + query.append(imap_attribute_lookup[name].format(value)) if query: - logger.debug("IMAP query: {}".format(" ".join(query))) return " ".join(query) - logger.debug("IMAP query: {}".format("(ALL)")) return "(ALL)" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/imbox-0.9.6/imbox/vendors/gmail.py new/imbox-0.9.7/imbox/vendors/gmail.py --- old/imbox-0.9.6/imbox/vendors/gmail.py 2018-08-14 17:22:47.000000000 +0200 +++ new/imbox-0.9.7/imbox/vendors/gmail.py 2020-05-03 13:55:23.000000000 +0200 @@ -1,4 +1,5 @@ from imbox.messages import Messages +from imbox.vendors.helpers import merge_two_dicts class GmailMessages(Messages): @@ -6,7 +7,7 @@ 'https://myaccount.google.com/apppasswords') hostname = 'imap.gmail.com' name = 'gmail' - folder_lookup = { + FOLDER_LOOKUP = { 'all_mail': '"[Gmail]/All Mail"', 'all': '"[Gmail]/All Mail"', @@ -19,11 +20,20 @@ 'spam': '"[Gmail]/Spam"', 'starred': '"[Gmail]/Starred"', 'trash': '"[Gmail]/Trash"', + } + GMAIL_IMAP_ATTRIBUTE_LOOKUP_DIFF = { + 'subject': '(X-GM-RAW "subject:\'{}\'")', + 'label': '(X-GM-LABELS "{}")', + 'raw': '(X-GM-RAW "{}")' } def __init__(self, connection, parser_policy, **kwargs): + + self.IMAP_ATTRIBUTE_LOOKUP = merge_two_dicts(self.IMAP_ATTRIBUTE_LOOKUP, + self.GMAIL_IMAP_ATTRIBUTE_LOOKUP_DIFF) + super().__init__(connection, parser_policy, **kwargs) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/imbox-0.9.6/imbox/vendors/helpers.py new/imbox-0.9.7/imbox/vendors/helpers.py --- old/imbox-0.9.6/imbox/vendors/helpers.py 1970-01-01 01:00:00.000000000 +0100 +++ new/imbox-0.9.7/imbox/vendors/helpers.py 2020-05-03 13:55:23.000000000 +0200 @@ -0,0 +1,6 @@ + +def merge_two_dicts(x, y): + """from https://stackoverflow.com/a/26853961/4386191""" + z = x.copy() # start with x's keys and values + z.update(y) # modifies z with y's keys and values & returns None + return z diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/imbox-0.9.6/imbox.egg-info/PKG-INFO new/imbox-0.9.7/imbox.egg-info/PKG-INFO --- old/imbox-0.9.6/imbox.egg-info/PKG-INFO 2018-08-14 17:26:01.000000000 +0200 +++ new/imbox-0.9.7/imbox.egg-info/PKG-INFO 2020-05-03 13:59:43.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: imbox -Version: 0.9.6 +Version: 0.9.7 Summary: Python IMAP for Human beings Home-page: https://github.com/martinrusev/imbox Author: Martin Rusev @@ -84,9 +84,16 @@ # Messages whose subjects contain a string inbox_messages_subject_christmas = imbox.messages(subject='Christmas') + # Messages whose UID is greater than 1050 + inbox_messages_uids_greater_than_1050 = imbox.messages(uid__range='1050:*') + # Messages from a specific folder messages_in_folder_social = imbox.messages(folder='Social') + # Some of Gmail's IMAP Extensions are supported (label and raw): + all_messages_with_an_attachment_from_martin = imbox.messages(folder='all', raw='from:mar...@amon.cx has:attachment') + all_messages_labeled_finance = imbox.messages(folder='all', label='finance') + for uid, message in all_inbox_messages: # Every message is an object with the following keys diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/imbox-0.9.6/imbox.egg-info/SOURCES.txt new/imbox-0.9.7/imbox.egg-info/SOURCES.txt --- old/imbox-0.9.6/imbox.egg-info/SOURCES.txt 2018-08-14 17:26:01.000000000 +0200 +++ new/imbox-0.9.7/imbox.egg-info/SOURCES.txt 2020-05-03 13:59:43.000000000 +0200 @@ -17,6 +17,7 @@ imbox.egg-info/top_level.txt imbox/vendors/__init__.py imbox/vendors/gmail.py +imbox/vendors/helpers.py tests/8422.msg tests/__init__.py tests/parser_tests.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/imbox-0.9.6/tests/query_tests.py new/imbox-0.9.7/tests/query_tests.py --- old/imbox-0.9.6/tests/query_tests.py 2018-08-14 17:22:47.000000000 +0200 +++ new/imbox-0.9.7/tests/query_tests.py 2020-05-03 13:55:23.000000000 +0200 @@ -1,50 +1,87 @@ +from datetime import date import unittest + from imbox.query import build_search_query -from datetime import date +from imbox.messages import Messages +from imbox.vendors.helpers import merge_two_dicts +from imbox.vendors.gmail import GmailMessages + +IMAP_ATTRIBUTE_LOOKUP = Messages.IMAP_ATTRIBUTE_LOOKUP +GMAIL_ATTRIBUTE_LOOKUP = merge_two_dicts(IMAP_ATTRIBUTE_LOOKUP, + GmailMessages.GMAIL_IMAP_ATTRIBUTE_LOOKUP_DIFF) class TestQuery(unittest.TestCase): def test_all(self): - res = build_search_query() + res = build_search_query(IMAP_ATTRIBUTE_LOOKUP) self.assertEqual(res, "(ALL)") + def test_subject(self): + + res = build_search_query(IMAP_ATTRIBUTE_LOOKUP, subject='hi') + self.assertEqual(res, '(SUBJECT "hi")') + + res = build_search_query(GMAIL_ATTRIBUTE_LOOKUP, subject='hi') + self.assertEqual(res, '(X-GM-RAW "subject:\'hi\'")') + def test_unread(self): - res = build_search_query(unread=True) + res = build_search_query(IMAP_ATTRIBUTE_LOOKUP, unread=True) self.assertEqual(res, "(UNSEEN)") def test_unflagged(self): - res = build_search_query(unflagged=True) + res = build_search_query(IMAP_ATTRIBUTE_LOOKUP, unflagged=True) self.assertEqual(res, "(UNFLAGGED)") def test_flagged(self): - res = build_search_query(flagged=True) + res = build_search_query(IMAP_ATTRIBUTE_LOOKUP, flagged=True) self.assertEqual(res, "(FLAGGED)") def test_sent_from(self): - res = build_search_query(sent_from='t...@example.com') + res = build_search_query( + IMAP_ATTRIBUTE_LOOKUP, sent_from='t...@example.com') self.assertEqual(res, '(FROM "t...@example.com")') def test_sent_to(self): - res = build_search_query(sent_to='t...@example.com') + res = build_search_query( + IMAP_ATTRIBUTE_LOOKUP, sent_to='t...@example.com') self.assertEqual(res, '(TO "t...@example.com")') def test_date__gt(self): - res = build_search_query(date__gt=date(2014, 12, 31)) + res = build_search_query( + IMAP_ATTRIBUTE_LOOKUP, date__gt=date(2014, 12, 31)) self.assertEqual(res, '(SINCE "31-Dec-2014")') def test_date__lt(self): - res = build_search_query(date__lt=date(2014, 1, 1)) + res = build_search_query( + IMAP_ATTRIBUTE_LOOKUP, date__lt=date(2014, 1, 1)) self.assertEqual(res, '(BEFORE "01-Jan-2014")') def test_date__on(self): - res = build_search_query(date__on=date(2014, 1, 1)) + res = build_search_query( + IMAP_ATTRIBUTE_LOOKUP, date__on=date(2014, 1, 1)) self.assertEqual(res, '(ON "01-Jan-2014")') + + def test_uid__range(self): + res = build_search_query(IMAP_ATTRIBUTE_LOOKUP, uid__range='1000:*') + self.assertEqual(res, '(UID 1000:*)') + + def test_text(self): + res = build_search_query(IMAP_ATTRIBUTE_LOOKUP, text='mail body') + self.assertEqual(res, '(TEXT "mail body")') + + def test_gmail_raw(self): + res = build_search_query(GMAIL_ATTRIBUTE_LOOKUP, raw='has:attachment subject:"hey"') + self.assertEqual(res, '(X-GM-RAW "has:attachment subject:\'hey\'")') + + def test_gmail_label(self): + res = build_search_query(GMAIL_ATTRIBUTE_LOOKUP, label='finance') + self.assertEqual(res, '(X-GM-LABELS "finance")')