From bbb6a8e94ae18e3b0144260cb54e5673dee12c69 Mon Sep 17 00:00:00 2001 From: Marc Fargas Date: Wed, 13 May 2009 17:07:35 +0200 Subject: [PATCH] Messing around with Tests so they run somehow. Added a README.rst --- README.rst | 92 +++++++++++++++++++++++++++ buildout.cfg | 4 +- setup.py | 28 ++++++++ src/mailserver/handlers.py | 2 +- src/mailserver/testapp/__init__.py | 0 src/mailserver/testapp/mailbox.py | 30 +++++++++ src/mailserver/testapp/mailers.py | 17 +++++ src/mailserver/testapp/models.py | 0 src/mailserver/testapp/settings.py | 6 ++ src/mailserver/{ => testapp}/tests.py | 8 ++- src/mailserver/testsettings.py | 5 -- 11 files changed, 182 insertions(+), 10 deletions(-) create mode 100644 README.rst create mode 100644 setup.py create mode 100644 src/mailserver/testapp/__init__.py create mode 100644 src/mailserver/testapp/mailbox.py create mode 100644 src/mailserver/testapp/mailers.py create mode 100644 src/mailserver/testapp/models.py create mode 100644 src/mailserver/testapp/settings.py rename src/mailserver/{ => testapp}/tests.py (88%) delete mode 100644 src/mailserver/testsettings.py diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..6788e37 --- /dev/null +++ b/README.rst @@ -0,0 +1,92 @@ +Develop Mailservices in Django +============================== + +Django Mailserver is an application that lets you develop +mail services in your Django projects/applications in a manner +that handles messages as Django handles HTTP requests. + +That project was initialy conceived as an improvement over +`django-smtpd`_ by Denis Laprise, then became an almost full rewrite +and when I thought I had gone through almost every line of code it +seemed far much more like a fork than improvement. + +NOTE: that this is really alpha software, don't use it in a production +environment unless you expect it to break everything ;) + +.. _django-smtpd:: http://code.google.com/p/django-smtpd/ + +Example usage: +============== + +First of all, you need to add "mailserver" in your INSTALLED_APPS, and +you'll want the settings:: + + ROOT_MAILCONF = 'myproject.mailbox' # Just like ROOT_URLCONF, but for + # resolving recipients. + MAILER_DAEMON_ADDRESS = 'postmaster@mydomain.com' + +``ROOT_MAILCONF`` is just like an URLCONF, but it matches e-mail addresses +instead of paths. An example ``myproject.mailbox`` ``mailbox.py`` file +would be:: + + from django_mailserver.mailbox import * + + urlpatterns = patterns('', + (r'@bugs.example.com', include('myapp.mailbox')), + ) + +Note, that when matching recipients, just as Django strips path elements +as they get matches, ``mailserver`` strips already matched parts from +the addresses until it reaches the views. + +Now you can start you mail service! You can either to:: + + ./manage.py startmailapp + +From your project directory to have a new app created with a sample mailbox.py +file in it, or you can create a new mailbox.py file in your existing application:: + + from django_mailserver.mailbox import * + + urlpatterns = patterns('', + (r'^onedest', 'myapp.mailers.reply'), + ) + +You have it, your ``mailers.py`` file would be just like a ``views.py``:: + + from mailserver import EmailResponse + + def reply(request): + print "Got email %s, Reply!" % request + return EmailResponse( + from_email=request.get_recipient_display(), + to=request['From'], + body=request.get_body(), + subject="Re: %s" % request['Subject']) + +You get the idea, if you return an EmailResponse it gets ``send()`` +later by the Handler. You can also return EmailIgnoreResponse among others. + +Delivering mails +**************** + +Right now the only possible way to deliver messages to this is through a +pipe transport to the ``./manage.py readmail`` command. Which is mostly +intended for testing. + +Further improvements should have a more performant pipe transport and a +self-running SMTP server. + +TODO +**** + +**WARNING:** Django Mailserver is still under development. It is believed to +brake at any point ;) There are lots of things to do, like: + + * Documentation + * More tests. + * Better URL parsing (i.e: includes work on domains, others on addreses). + * Handling address prefixes/suffixes + * Handling of error responses (ie: pipe transport should bring the + response status_code to the exit value of the process). + diff --git a/buildout.cfg b/buildout.cfg index 115688a..c82887d 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -13,6 +13,6 @@ recipe = djangorecipe version = 1.0.2 project = mailserver projectegg = mailserver -settings = testsettings -test = mailserver +settings = testapp.settings +test = testapp eggs = ${buildout:eggs} diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..c59e01c --- /dev/null +++ b/setup.py @@ -0,0 +1,28 @@ +import os +from setuptools import setup, find_packages + +def read(fname): + return open(os.path.join(os.path.dirname(__file__), fname)).read() + +setup( + name = "mailserver", + version = "0.1alpha1", + url = 'http://github.com/telenieko/django-mailserver', + license = 'BSD', + description = "Create mail services in Django.", + long_description = read('README.rst'), + author = 'Marc Fargas', + author_email = 'telenieko@telenieko.com', + packages = find_packages('src'), + package_dir = {'': 'src'}, + install_requires = ['setuptools'], + classifiers = [ + 'Development Status :: 4 - Beta', + 'Framework :: Django', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Topic :: Internet :: WWW/HTTP', + ] +) diff --git a/src/mailserver/handlers.py b/src/mailserver/handlers.py index f6a57d1..5d62b8d 100644 --- a/src/mailserver/handlers.py +++ b/src/mailserver/handlers.py @@ -73,7 +73,7 @@ def handle_uncaught_exception(self, request, resolver, exc_info): raise # When DEBUG is False, send an error message to the admins. - subject = 'Mail Error (%s)' % (request['From']) + subject = 'Mail Error (%s)' % request['From'][1] try: request_repr = repr(request) except: diff --git a/src/mailserver/testapp/__init__.py b/src/mailserver/testapp/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/mailserver/testapp/mailbox.py b/src/mailserver/testapp/mailbox.py new file mode 100644 index 0000000..b37a5ea --- /dev/null +++ b/src/mailserver/testapp/mailbox.py @@ -0,0 +1,30 @@ +from mailserver.mailbox import * +import sys +import types + +testapp = types.ModuleType('testapp2') +testappboxes = types.ModuleType('mailbox') +testapp.mailbox = testappboxes +sys.modules.setdefault('testapp2', testapp) +sys.modules.setdefault('testapp2.mailbox', testappboxes) + +testpatterns = patterns('', + (r'^onedest', 'testapp.mailers.reply'), + (r'(?P.+)@(?P.*)', 'testapp.mailers.echo'), + (r'(?P.+)', 'testapp.mailers.echo'), + ) +testappboxes.urlpatterns = testpatterns + +urlpatterns = patterns('', + (r'(\.?)example.net', include('testapp2.mailbox')), + (r'@bugs.example.com', include('testapp2.mailbox')), +) + +def echo(request, sender, domain=None): + print 'received sender=%s domain=%s' % (sender, domain) + resp = EmailResponseServer(from_email=request.get_recipient_display(), + to=request['From'], body=sender, + subject="Echo Echo") + resp.sender = sender + resp.domain = domain + return resp diff --git a/src/mailserver/testapp/mailers.py b/src/mailserver/testapp/mailers.py new file mode 100644 index 0000000..9a629ff --- /dev/null +++ b/src/mailserver/testapp/mailers.py @@ -0,0 +1,17 @@ +from mailserver import EmailResponse, EmailIgnoreResponse, EmailResponseServer + +def echo(request, sender, domain=None): + print 'received sender=%s domain=%s' % (sender, domain) + resp = EmailResponseServer(from_email=request.get_recipient_display(), + to=request['From'], body=sender, + subject="Echo Echo") + resp.sender = sender + resp.domain = domain + return resp + +def reply(request): + print "Got email %s, mirror-reply!" % request + return EmailResponse(from_email=request.get_recipient_display(), + to=request['From'], + body=request.get_body(), + subject="Re: %s" % request['Subject']) diff --git a/src/mailserver/testapp/models.py b/src/mailserver/testapp/models.py new file mode 100644 index 0000000..e69de29 diff --git a/src/mailserver/testapp/settings.py b/src/mailserver/testapp/settings.py new file mode 100644 index 0000000..26196c2 --- /dev/null +++ b/src/mailserver/testapp/settings.py @@ -0,0 +1,6 @@ +DATABASE_ENGINE = 'sqlite3' +DATABASE_NAME = ':memory:' +INSTALLED_APPS = ['mailserver', 'mailserver.testapp'] +ROOT_URLCONF = 'mailserver.urls' +ROOT_MAILCONF = 'mailserver.testapp.mailbox' +DEBUG_PROPAGATE_EXCEPTIONS = True diff --git a/src/mailserver/tests.py b/src/mailserver/testapp/tests.py similarity index 88% rename from src/mailserver/tests.py rename to src/mailserver/testapp/tests.py index 21cc101..1e9d701 100644 --- a/src/mailserver/tests.py +++ b/src/mailserver/testapp/tests.py @@ -1,9 +1,11 @@ import unittest import os, sys + from email import message_from_string from django.test import TestCase from mailserver import * from mailserver.handlers import BaseMessageHandler +from django.template import TemplateDoesNotExist MAIL = """From marc@telenieko.com Wed Oct 22 07:16:19 2008 Return-path: @@ -27,8 +29,10 @@ def test_404(self): self.message.replace_header('Envelope-To', 'johndoe@example.org') em = EmailRequest(message=self.message) handler = BaseMessageHandler() - res = handler(os.environ, em) - assert isinstance(res, EmailResponseNotFound) + try: + res = handler(os.environ, em) + except TemplateDoesNotExist, e: + self.assertEquals(e.message, 'recipient_notfound.txt') def handle_for(self, to): self.message.replace_header('Envelope-To', to) diff --git a/src/mailserver/testsettings.py b/src/mailserver/testsettings.py deleted file mode 100644 index 9187abe..0000000 --- a/src/mailserver/testsettings.py +++ /dev/null @@ -1,5 +0,0 @@ -DATABASE_ENGINE = 'sqlite3' -DATABASE_NAME = ':memory:' -INSTALLED_APPS = ['mailserver'] -ROOT_URLCONF = ['mailserver.urls'] -ROOT_MAILCONF = ['mailserver.mailbox']