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 :)
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.
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.
> 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.
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!)
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.
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:
In this case, 'foo={}' is evaluated once, and returns a pointer to a dict(). After that, foo is always pointing to the same dict, not to a newly created empty dict.
The things he's talking about have nothing to do with Python. If you have a per-application instance of a class, you can't use instance data for request-specific things. The reason is because the per-application class is per-application, not per-request. (If you clone the per-application class for each request, then it's OK, but still a little weird. Any operation that needs state to be initialized and thrown away is exactly why you create an instance of a class.)
Really, anything that changes instance data outside the constructor should worry you. And actually, Python's habit of requiring you to initialize attributes in __init__ is kind of annoying; stuff like Perl's Moose is much cleaner.
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 :)
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.
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!
25 comments
[ 2.9 ms ] story [ 26.1 ms ] threadFor 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 :)
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.
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.
(We have an internal library at work that does something like:
Guess what happens when you change some_instance.foo. Yup, all the other instances change 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
Run this in a Python interpreter:
The only thing that is really safe to put in the defaults are immutable values.def subscribe(self, route=r'subscribe-(?P<name>[^@])@.'):
with:
@route('subscribe-(?P<name>[^@])@.'')
def subscribe(self):
and we can stop arguing.
I'm sticking to my guns on the instance variables, it's cleaner.
Really, anything that changes instance data outside the constructor should worry you. And actually, Python's habit of requiring you to initialize attributes in __init__ is kind of annoying; stuff like Perl's Moose is much cleaner.
https://github.com/bcoe/smtproutes/issues/1
Allow me to amend "What if SMTP and Sinatra had a Baby (Which happend to be implemented in Python)".
Anyway, yes, a multi-user email service is not trivial to run. But email itself is pretty easy to work with, modulo RFC822, anyway :)
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.
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!