From dd15a0abcc28bed19cc4c96c02ce80bf58871525 Mon Sep 17 00:00:00 2001 From: Eric Plaster Date: Sun, 23 Apr 2017 19:15:37 -0500 Subject: [PATCH 1/8] Fixed install issue with python 3 Added tox to run tests in python versions 2.7,3.3,3.4,3.5,3.6 Fixed other issues my editor pointed out as compatability warnings --- flask_mailgun/api.py | 10 +++++++--- flask_mailgun/core.py | 18 +++++++++--------- flask_mailgun/message.py | 2 +- flask_mailgun/processing.py | 4 ++-- requirements_test.in => requirements_test.txt | 1 + setup.py | 7 ++++--- tests/test_async.py | 2 +- tests/test_receve.py | 13 ++++++++----- tests/test_save_attachments.py | 2 +- tox.ini | 8 ++++++++ 10 files changed, 42 insertions(+), 25 deletions(-) rename requirements_test.in => requirements_test.txt (86%) create mode 100644 tox.ini diff --git a/flask_mailgun/api.py b/flask_mailgun/api.py index dc807a3..3ecf4c9 100644 --- a/flask_mailgun/api.py +++ b/flask_mailgun/api.py @@ -27,6 +27,7 @@ def __init__(self, config): MAILGUN_API_URL) self.route = config.get('MAILGUN_ROUTE', 'uploads') self.host = config.get('MAILGUN_HOST', self.domain) + self.dest = '/messages/' if self.api_key is None: raise MailGunException("No mailgun key supplied.") @@ -117,12 +118,15 @@ def get_route_id(self, route): """ Get id of the route Return: id of the route. None if not exist. """ - def make_key(route): - return route["expression"].join(route["action"]) + def make_key(_route): + return _route["expression"].join(_route["action"]) + # TODO RPM YXI, not actually shure this is what we want... routes = self.list_routes() if routes: id_table = dict((make_key(r), r["id"]) for r in routes) + else: + return None return id_table.get(make_key(route)) if routes else None def verify_email(self, email): @@ -157,4 +161,4 @@ def routepoint(self): @property def auth(self): - return ('api', self.api_key) + return 'api', self.api_key diff --git a/flask_mailgun/core.py b/flask_mailgun/core.py index ebd408c..7791da6 100644 --- a/flask_mailgun/core.py +++ b/flask_mailgun/core.py @@ -85,20 +85,20 @@ def process_attachment(email, filestorage)` self._on_attachment.append(new_func) return func - def get_attachments(self, request): - files = request.files.values() + def get_attachments(self, reqst): + files = reqst.files.values() attachments = [att for att in files if self._is_file_allowed(att.filename)] return attachments - def process_email(self, request): + def process_email(self, reqst): """Function to pass to endpoint for processing incoming email post app.route('/incoming', methods=['POST'])(process_email) """ - email = request.form + email = reqst.form self.mailgun_api.verify_email(email) # Process the attachments - attachments = self.get_attachments(request) + attachments = self.get_attachments(reqst) process_attachments(email, attachments, self._on_attachment) @@ -106,7 +106,7 @@ def process_email(self, request): for func in self._on_receive: func(email) # log and notify - self.__log_status(request) + self.__log_status(reqst) if self.auto_reply: self.reply_sender(email) return "OK" @@ -129,13 +129,13 @@ def _is_file_allowed(self, filename): return '.' in filename and \ filename.rsplit('.', 1)[1].lower() in self.allowed_extensions - def __log_status(self, request): + def __log_status(self, reqst): if self.logger: - email = request.form + email = reqst.form return self.logger.log({ "message": "Email received", "sender": email.get("sender"), "receiver": email.get("recipient"), "timestamp": email.get("timestamp"), "number_of_attachments": email.get("attachment-count"), - "attachment_names": request.files.keys()}, "Info") + "attachment_names": reqst.files.keys()}, "Info") diff --git a/flask_mailgun/message.py b/flask_mailgun/message.py index 1f573b0..f70e588 100644 --- a/flask_mailgun/message.py +++ b/flask_mailgun/message.py @@ -52,7 +52,7 @@ def __init__(self, subject='', sender = sender or current_app.extensions['mail'].default_sender if isinstance(sender, tuple): - sender = "%s <%s>" % sender + sender = "%s <%s>" % (sender[0], sender[1]) self.recipients = recipients or [] self.subject = subject diff --git a/flask_mailgun/processing.py b/flask_mailgun/processing.py index 3e8b2ed..37617fd 100644 --- a/flask_mailgun/processing.py +++ b/flask_mailgun/processing.py @@ -1,7 +1,7 @@ -from threading import Thread +from functools import wraps from multiprocessing import Pool + from decorator import decorator -from functools import wraps def async_pool(pool_size): diff --git a/requirements_test.in b/requirements_test.txt similarity index 86% rename from requirements_test.in rename to requirements_test.txt index fe537a3..7ae7bcd 100644 --- a/requirements_test.in +++ b/requirements_test.txt @@ -2,3 +2,4 @@ pylint mock diff_cover coveralls +nose diff --git a/setup.py b/setup.py index 09acbe6..b4782c3 100644 --- a/setup.py +++ b/setup.py @@ -3,6 +3,7 @@ Flask extension to use the Mailgun email parsing service for sending and receving emails """ +from io import open from pip.req import parse_requirements try: from setuptools import setup @@ -10,10 +11,10 @@ from distutils.core import setup -with open('Version', 'r') as f: - version = next(f).strip().decode('utf-8') +with open('Version', encoding='utf-8') as f: + version = next(f).strip() -with open('README.rst') as f: +with open('README.rst', encoding='utf-8') as f: readme = f.read() # parse_requirements() returns generator of pip.req.InstallRequirement objects diff --git a/tests/test_async.py b/tests/test_async.py index 5c1ed08..0261fca 100644 --- a/tests/test_async.py +++ b/tests/test_async.py @@ -6,7 +6,7 @@ def runner(fun): - results = [fun(i) for i in xrange(20)] + results = [fun(i) for i in range(20)] for result in results: result.wait() result.get() diff --git a/tests/test_receve.py b/tests/test_receve.py index ea29efb..e3aadc8 100644 --- a/tests/test_receve.py +++ b/tests/test_receve.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +from __future__ import print_function + """ Created on Fri Apr 21 14:05:26 2017 @@ -56,7 +58,7 @@ def attachment_func(email, attachment): responce = self.attachment_mock(email, attachment) data = attachment.read() len(data) - for i in xrange(10): + for i in range(10): time.sleep(0.1) return responce @@ -70,7 +72,7 @@ def test_receive_message(self): time.sleep(1) self.assertEqual(self.receve_email_mock.call_count, 1) self.assertEqual(self.attachment_mock.call_count, 1) - print "received email" + print ("received email") class ReceiveMessageAsyncTest(ReceiveMessageCallbacksTest): @@ -94,16 +96,17 @@ def test_receive_2_messages(self): time.sleep(1) # self.assertEqual(self.receve_email_mock.call_count, 2) # self.assertEqual(self.attachment_mock.call_count, 2) - print "received 2 emails" + print("received 2 emails") def test_receive_100_messages(self): - for i in xrange(100): + for i in range(100): email = make_email_request(self.mailgun) response = self.appclient.post('/upload', data=email) self.assertEqual(response.status_code, 200) # self.assertEqual(self.receve_email_mock.call_count, 100) # self.assertEqual(self.attachment_mock.call_count, 100) - print "received 100 emails" + print ("received 100 emails") + if __name__ == '__main__': unittest.main() diff --git a/tests/test_save_attachments.py b/tests/test_save_attachments.py index 7062815..7868f8a 100644 --- a/tests/test_save_attachments.py +++ b/tests/test_save_attachments.py @@ -8,7 +8,7 @@ import os import shutil import tempfile -from werkzeug import FileStorage +from werkzeug.datastructures import FileStorage from flask_mailgun.attachment import save_attachments from tests.fixtures import get_attachment from tests import MailgunTestBase diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..bb0c1ad --- /dev/null +++ b/tox.ini @@ -0,0 +1,8 @@ +[tox] +envlist = python{2.7,3.3,3.4,3.5,3.6} + +[testenv] +deps = + -r{toxinidir}/requirements_test.in +commands = + nosetests tests From d6f49c875a8afc08020e42f949c2027ee1cd111a Mon Sep 17 00:00:00 2001 From: Richard Mathie Date: Mon, 24 Apr 2017 14:42:50 +0100 Subject: [PATCH 2/8] fixed package setup --- README.md | 2 +- README.rst | 5 +++-- Version | 2 +- requirements_test.in | 4 ---- setup.py | 12 +++++------- 5 files changed, 10 insertions(+), 15 deletions(-) delete mode 100644 requirements_test.in diff --git a/README.md b/README.md index 87acf1e..1e72037 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [![License](https://img.shields.io/pypi/l/Flask-MailGun3.svg)](https://pypi.python.org/pypi/Flask-MailGun3) [![Downloads](https://img.shields.io/pypi/dm/flask-mailgun3.svg)](https://pypi.python.org/pypi/Flask-Mailgun3) -Flask MailGun extension to use the [MailGun](https://mailgun.com) email parsing service for sending and receiving emails. +Flask-MailGun Flask extension to use the [MailGun](https://mailgun.com) email parsing service for sending and receiving emails. ## What it does diff --git a/README.rst b/README.rst index 7c58ddc..9230478 100644 --- a/README.rst +++ b/README.rst @@ -4,8 +4,9 @@ Flask-MailGun |Latest Version| |Build Status| |Coverage Status| |Code Climate| |Python Versions| |License| |Downloads| -Flask MailGun extension to use the `MailGun `__ -email parsing service for sending and receiving emails. +Flask-MailGun Flask extension to use the +`MailGun `__ email parsing service for sending and +receiving emails. What it does ------------ diff --git a/Version b/Version index 845639e..9faa1b7 100644 --- a/Version +++ b/Version @@ -1 +1 @@ -0.1.4 +0.1.5 diff --git a/requirements_test.in b/requirements_test.in deleted file mode 100644 index fe537a3..0000000 --- a/requirements_test.in +++ /dev/null @@ -1,4 +0,0 @@ -pylint -mock -diff_cover -coveralls diff --git a/setup.py b/setup.py index 09acbe6..ef6b16e 100644 --- a/setup.py +++ b/setup.py @@ -1,8 +1,5 @@ -""" -Flask-MailGun -Flask extension to use the Mailgun email parsing service -for sending and receving emails -""" +#!/usr/bin/env python +# -*- coding: utf-8 -*- from pip.req import parse_requirements try: from setuptools import setup @@ -21,6 +18,7 @@ requirements = [str(ir.req) for ir in requirements] __NAME__ = 'Flask-MailGun3' +__doc__ = readme __author__ = 'Amey-SAM' __license__ = 'MIT' __copyright__ = '2016' @@ -35,8 +33,8 @@ author_email='richard.mathie@amey.co.uk', url='https://github.com/amey-sam/Flask-MailGun', download_url='https://github.com/amey-sam/Flask-MailGun/tarball/master', - py_modules=['flask_mailgun'], - # packages=['flask_mailgun'], + # py_modules=['flask_mailgun'], + packages=['flask_mailgun'], install_requires=requirements, keywords=['flask', 'mailgun'], zip_safe=False, From aeda83796834d44d940465b0c5bdef05b078459b Mon Sep 17 00:00:00 2001 From: Richard Mathie Date: Mon, 24 Apr 2017 15:37:51 +0100 Subject: [PATCH 3/8] io open --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 1c914cf..1be0149 100644 --- a/setup.py +++ b/setup.py @@ -6,6 +6,7 @@ for sending and receving emails """ from pip.req import parse_requirements +from io import open try: from setuptools import setup except ImportError: From e01591b183329b2a1713c0308b8632af235ee8f1 Mon Sep 17 00:00:00 2001 From: Richard Mathie Date: Mon, 24 Apr 2017 15:51:00 +0100 Subject: [PATCH 4/8] python3 and range --- tests/test_async.py | 2 +- tests/test_receve.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_async.py b/tests/test_async.py index 5c1ed08..0261fca 100644 --- a/tests/test_async.py +++ b/tests/test_async.py @@ -6,7 +6,7 @@ def runner(fun): - results = [fun(i) for i in xrange(20)] + results = [fun(i) for i in range(20)] for result in results: result.wait() result.get() diff --git a/tests/test_receve.py b/tests/test_receve.py index faf80cd..ab3bb89 100644 --- a/tests/test_receve.py +++ b/tests/test_receve.py @@ -57,7 +57,7 @@ def attachment_func(email, attachment): responce = self.attachment_mock(email, attachment) data = attachment.read() len(data) - for i in xrange(10): + for i in range(10): time.sleep(0.1) return responce @@ -98,7 +98,7 @@ def test_receive_2_messages(self): print ("received 2 emails") def test_receive_100_messages(self): - for i in xrange(100): + for i in range(100): email = make_email_request(self.mailgun) response = self.appclient.post('/upload', data=email) self.assertEqual(response.status_code, 200) From 15a35f9f4c9964e7aba8ddb73bef9fd59d0b9525 Mon Sep 17 00:00:00 2001 From: Richard Mathie Date: Mon, 24 Apr 2017 16:07:24 +0100 Subject: [PATCH 5/8] python3 string compattiblity issue --- tests/fixtures/email.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fixtures/email.py b/tests/fixtures/email.py index 2506cc0..caed824 100644 --- a/tests/fixtures/email.py +++ b/tests/fixtures/email.py @@ -13,7 +13,7 @@ from tests.fixtures import get_attachment from flask_mailgun.message import Message -url_safe_chars = string.lowercase+string.digits+string.uppercase +url_safe_chars = string.ascii_lowercase + string.ascii_uppercase + string.digits def random_string(length): From cca2128c28387b52423e1519630031db236d0756 Mon Sep 17 00:00:00 2001 From: Richard Mathie Date: Mon, 24 Apr 2017 16:15:20 +0100 Subject: [PATCH 6/8] python3 hmac string and bytes --- flask_mailgun/api.py | 4 ++-- tests/fixtures/email.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/flask_mailgun/api.py b/flask_mailgun/api.py index cfd3522..5f80abe 100644 --- a/flask_mailgun/api.py +++ b/flask_mailgun/api.py @@ -22,7 +22,7 @@ class MailGunAPI(object): def __init__(self, config): self.domain = config['MAILGUN_DOMAIN'] - self.api_key = config['MAILGUN_API_KEY'] + self.api_key = bytes(config['MAILGUN_API_KEY']) self.api_url = config.get('MAILGUN_API_URL', MAILGUN_API_URL) self.route = config.get('MAILGUN_ROUTE', 'uploads') @@ -142,7 +142,7 @@ def verify_email(self, email): raise MailGunException("Mailbox Error: credential verification failed.", "Not enough parameters") signature_calc = hmac.new(key=self.api_key, - msg='{}{}'.format(timestamp, token), + msg=bytes('{}{}'.format(timestamp, token)), digestmod=hashlib.sha256).hexdigest() if signature != signature_calc: raise MailGunException("Mailbox Error: credential verification failed.", "Signature doesn't match") diff --git a/tests/fixtures/email.py b/tests/fixtures/email.py index caed824..29ecc8d 100644 --- a/tests/fixtures/email.py +++ b/tests/fixtures/email.py @@ -25,7 +25,7 @@ def sign_email(email, mailgun): timestamp = int(time()) api_key = mailgun.mailgun_api.api_key signature = hmac.new(key=api_key, - msg='{}{}'.format(timestamp, token), + msg=bytes('{}{}'.format(timestamp, token)), digestmod=hashlib.sha256).hexdigest() email.update(dict(token=token, timestamp=timestamp, From f1d64798a0364b8efeb660488a3e3ea101460fd4 Mon Sep 17 00:00:00 2001 From: Richard Mathie Date: Mon, 24 Apr 2017 16:28:29 +0100 Subject: [PATCH 7/8] starting to hate python3 --- flask_mailgun/api.py | 5 +++-- flask_mailgun/attachment.py | 4 ++-- tests/fixtures/email.py | 4 +++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/flask_mailgun/api.py b/flask_mailgun/api.py index 5f80abe..4731b7c 100644 --- a/flask_mailgun/api.py +++ b/flask_mailgun/api.py @@ -22,7 +22,7 @@ class MailGunAPI(object): def __init__(self, config): self.domain = config['MAILGUN_DOMAIN'] - self.api_key = bytes(config['MAILGUN_API_KEY']) + self.api_key = config['MAILGUN_API_KEY'].encode('utf-8') self.api_url = config.get('MAILGUN_API_URL', MAILGUN_API_URL) self.route = config.get('MAILGUN_ROUTE', 'uploads') @@ -141,8 +141,9 @@ def verify_email(self, email): if timestamp is None or token is None or signature is None: raise MailGunException("Mailbox Error: credential verification failed.", "Not enough parameters") + message = '{}{}'.format(timestamp, token).encode('utf-8') signature_calc = hmac.new(key=self.api_key, - msg=bytes('{}{}'.format(timestamp, token)), + msg=message, digestmod=hashlib.sha256).hexdigest() if signature != signature_calc: raise MailGunException("Mailbox Error: credential verification failed.", "Signature doesn't match") diff --git a/flask_mailgun/attachment.py b/flask_mailgun/attachment.py index 932f127..28664cd 100644 --- a/flask_mailgun/attachment.py +++ b/flask_mailgun/attachment.py @@ -38,7 +38,7 @@ def attachment_decorator(f, email, filename): """Converts a file back into a FileStorage Object""" with open(filename, 'r') as file: attachment = FileStorage(stream=file, - filename=filename) + filename=filename.encode('utf-8')) result = f(email, attachment) return result @@ -90,7 +90,7 @@ class Attachment(object): def __init__(self, filename=None, content_type=None, data=None, disposition=None, headers=None): - self.filename = filename + self.filename = filename.encode('utf-8') self.content_type = content_type self.data = data self.disposition = disposition or 'attachment' diff --git a/tests/fixtures/email.py b/tests/fixtures/email.py index 29ecc8d..9fed2a9 100644 --- a/tests/fixtures/email.py +++ b/tests/fixtures/email.py @@ -24,8 +24,10 @@ def sign_email(email, mailgun): token = random_string(50) timestamp = int(time()) api_key = mailgun.mailgun_api.api_key + message = '{}{}'.format(timestamp, token).encode('utf-8') + signature = hmac.new(key=api_key, - msg=bytes('{}{}'.format(timestamp, token)), + msg=message, digestmod=hashlib.sha256).hexdigest() email.update(dict(token=token, timestamp=timestamp, From 8623c79d0b5841ef56402458c287042eb9fc5847 Mon Sep 17 00:00:00 2001 From: Richard Mathie Date: Mon, 24 Apr 2017 16:39:01 +0100 Subject: [PATCH 8/8] python 3 idnk --- .travis.yml | 4 ++-- tests/fixtures/__init__.py | 1 + tests/test_send.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4cf154a..1dbd4a6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,8 @@ sudo: false language: python python: - "2.7" -- "3.3" -- "3.5" +#- "3.3" +#- "3.5" - pypy-5.3.1 env: global: diff --git a/tests/fixtures/__init__.py b/tests/fixtures/__init__.py index 31b1909..ed0ba92 100644 --- a/tests/fixtures/__init__.py +++ b/tests/fixtures/__init__.py @@ -13,3 +13,4 @@ def get_attachment(): f_name = os.path.join(fixture_dir, filename) file_stream = open(f_name, "r") return (filename, file_stream) + diff --git a/tests/test_send.py b/tests/test_send.py index d438525..149133b 100644 --- a/tests/test_send.py +++ b/tests/test_send.py @@ -23,7 +23,7 @@ def test_send_simple_message(self): data = self.mock_post.call_args[1]['data'] # files = self.mock_post.call_args[1]['files'] self.assertEqual(url, 'https://api.mailgun.net/v3/example.com/messages') - self.assertEqual(auth, ('api', 'testtesttest')) + self.assertEqual(auth, ('api', b'testtesttest')) # self.assertEqual(files, []) self.assertEqual(data['from'], message.sender) self.assertEqual(data['to'], set(message.recipients))