

What if SMTP and Sinatra Had a Baby? - BenjaminCoe
http://www.benjamincoe.com/post/13375872364/what-if-smtp-and-sinatra-had-a-baby

======
LeafStorm
The title ("SMTP and Sinatra") made me expect something written in Ruby. Also:

    
    
        def subscribe(self, route=r'subscribe-(?P<name>[^@]*)@.*'):
    

No. No. No. No. No. No. _No._ Default arguments are _not_ for specifying
metadata about your functions. That's what decorators are for. And for the
love of Guido, why are you accessing per-request information from instance
variables? It seems to me that you would much rather be programming this in
Ruby.

~~~
BenjaminCoe
Lol, it's dogmatism like that which gives the Python community such a great
reputation.

I was shooting for the most concise syntax I could get. Why Python and not
Ruby? I wanted to use Python's standard smtpd library, and their great
libraries for dealing with RFC2822 emails.

Oh, in regards to the instance variables. It uses a forking based paradigm to
handle requests, so the instance variables are not maintained from request to
request.

~~~
LeafStorm
> I was shooting for the most concise syntax I could get.

Yes, but default arguments are simply _not intended for that._ Decorators
_are._ You wouldn't use default arguments as method metadata in Ruby, would
you? Going for the "most concise syntax" in all cases gets you Perl, and
Python is most definitely not Perl.

~~~
jrockway
In Perl, all arguments are undef (similar to None) by default, which is pretty
much the only thing you should be defaulting things to in Python. So actually,
Perl makes it hard to shoot yourself in the foot, while Python has syntax for
making it easy.

(We have an internal library at work that does something like:

    
    
        def __init__(self, ..., foo={}):
            self.foo = foo
    

Guess what happens when you change some_instance.foo. Yup, all the other
instances change too!)

~~~
LeafStorm
That's really not the point I was going for. I mean, we can debate which
language's style is better all day, but regardless of which approach to
default arguments is better Mr. Coe is _still_ using the ones in Python
improperly.

~~~
BenjaminCoe
I attempted to make amends one comment below this. I will switch the library
to using a decorator based approach -- it's as clean, and there's no reason
for me to break convention (I'll lash myself a few times to be safe too ;)

With regards to instance variables:

On a request, the main process forks and a new instance of the route object is
instantiated. Trust me, instance variables won't leak between requests:

This is what happens when a request comes in, the parameters to
process_message are described here:
<http://docs.python.org/library/smtpd.html>

    
    
        @pytoad_decorator()
        def process_message(self, peer, mailfrom, rcpttos, message_data):
            for RouteClass in self.routes:
                route = RouteClass(peer_ip=peer[0])
                route.route(message_data)

------
viraptor
Isn't this pretty much what Lamson (<http://lamsonproject.org/>) is aiming
for?

~~~
BenjaminCoe
I wanted something even simpler than Lamson. I think a good analogy would be
that Lamson is to Rails, as smtproutes is to Sinatra -- hence the title.

------
jrockway
The quote from the ubuntu docs is bullshit. Setting up a mail server does
require many different programs; an SMTP server, a spam filter, a virus
scanner, procmail, an IMAP server, and SMTP server, and so on. But if you just
want to play with SMTP, all you need is an SMTP server. And if you just want
to play with email, all you need is a program that reads a message from STDIN
and writes the result to STDOUT, because you can just run that from procmail.

Anyway, yes, a multi-user email service is not trivial to run. But email
itself is pretty easy to work with, modulo RFC822, anyway :)

~~~
BenjaminCoe
You definitely caught me pulling a sensational quote from the Ubuntu docs, for
sex appeal (I wonder if that's the first time such an act has occurred?)

What I was shooting for was a very minimal library that could leverage
Python's pretty killer standard email libraries -- as an example they do have
wonderful RFC2822 support in their standard libraries.

smtproutes is a very minimal library for people who want a bit more
programatic composition than bash scripts, and less overhead than, say
Postfix, dovecot, whatever.

------
Titanous
Mailman (my 2010 Ruby Summer of Code project) _is_ Sinatra for incoming email:
<https://github.com/titanous/mailman>

~~~
joshu
The name "mailman" is already used by a significant mailing list project.

------
inopinatus
I do believe these kind of behaviours are what makes Mailgun sexy.

~~~
BenjaminCoe
Yeah, definitely. Mailgun is one of the cooler services I've seen being built
on top of email.

For those who haven't heard of Mailgun, it basically lets you setup HTTP
webhooks that fire as a result of SMTP events. Great service for letting you
compose your own email-centric applications :)

------
MostAwesomeDude
So, I couldn't help but notice that you wrote this:

    
    
        $ easy_install smtproutes
    

I think there's a couple typos in there. For example, you could have spelled
it this way:

    
    
        $ pip install smtproutes
    

Using pip (<http://pypi.python.org/pypi/pip>) makes you a better ecosystem
contributor. Also, I noticed you're internally using asyncore, AKA Medusa.
This is a Bad Idea, period; you won't scale and you're using what is
essentially dead, horrid code. Consider rewording the statement entirely:

    
    
        $ pip install Twisted
        $ twistd mail ...
    

Now you have a _real_ SMTP/POP3/IMAP server at your disposal.

But yeah, I'd so much rather that you not use asyncore. It makes us cry. I'd
even let you keep easy_install if you compromise and drop asyncore!

