Python

wtforms-tornado: WTForms extensions for Tornado

Earlier this year I took up the development of a simple CMS for blogging, this project was just an attempt to play with Tornado and MongoDB, and one issue that was bugging me was the validation of the arguments of the HTTP request, I had to do nested and gigantic conditions, it was something really annoying. I started with something like this:

import tornado.web
import tornado.ioloop

class RegisterHandler(tornado.web.RequestHandler):

    def post(self):
        username = self.get_argument('username', None)
        password = self.get_argument('password', None)
        if username and password:
            self.write('Ok!')
        else:
            self.write('Invalid request!')

if __name__ == '__main__':
    tornado.web.Application([('/', RegisterHandler)]).listen(8888)
    tornado.ioloop.IOLoop.instance().start()

And as you can see this is quite basic, and what about if I want to validate data types, length, a regular expression, choices, etc, etc, etc, it would be really extensive.

The first thing I thought as a solution for this was WTForms, WTForms would help me to avoid these conditions and form fields rendering in templates. But what happened when I started to use it?

import tornado.web
import tornado.ioloop
import wtforms

from wtforms.fields import StringField, PasswordField
from wtforms.validators import Required

class RegisterForm(wtforms.Form):

    username = StringField(validators=[Required()])
    password = PasswordField(validators=[Required()])

class RegisterHandler(tornado.web.RequestHandler):

    def post(self):
        form = RegisterForm(self.request.arguments)
        if form.validate():
            self.write('Ok!')
        else:
            self.write('Invalid request!')

if __name__ == '__main__':
    tornado.web.Application([('/', RegisterHandler)]).listen(8888)
    tornado.ioloop.IOLoop.instance().start()

It looks very simple to do but the arguments attribute of the request is a naive dictionary and is not what WTForms expects to access the data. The exception and traceback obtained by invoking the validate method were:

ERROR:tornado.application:Uncaught exception POST / (127.0.0.1)
HTTPRequest(protocol='http', host='localhost:8888', method='POST', uri='/', version='HTTP/1.1', remote_ip='127.0.0.1', headers={'Host': 'localhost:8888', 'Accept': '*/*', 'User-Agent': 'curl/7.29.0'})
Traceback (most recent call last):
  File "/home/puentesarrin/virtualenvs/wtforms-tornado/local/lib/python2.7/site-packages/tornado/web.py", line 1141, in _when_complete
    callback()
  File "/home/puentesarrin/virtualenvs/wtforms-tornado/local/lib/python2.7/site-packages/tornado/web.py", line 1162, in _execute_method
    self._when_complete(method(*self.path_args, **self.path_kwargs),
  File "wtforms-tornado.py", line 18, in post
    form = RegisterForm(self.request.arguments)
  File "/home/puentesarrin/virtualenvs/wtforms-tornado/local/lib/python2.7/site-packages/wtforms/form.py", line 178, in __call__
    return type.__call__(cls, *args, **kwargs)
  File "/home/puentesarrin/virtualenvs/wtforms-tornado/local/lib/python2.7/site-packages/wtforms/form.py", line 233, in __init__
    self.process(formdata, obj, **kwargs)
  File "/home/puentesarrin/virtualenvs/wtforms-tornado/local/lib/python2.7/site-packages/wtforms/form.py", line 102, in process
    raise TypeError("formdata should be a multidict-type wrapper that supports the 'getlist' method")
TypeError: formdata should be a multidict-type wrapper that supports the 'getlist' method
ERROR:tornado.access:500 POST / (127.0.0.1) 15.63ms

Well, arguments doesn’t implement the “getlist” method, which is expected to return a list of values ​​for a given key. So I sent a pull request two weeks ago, for adding an extension to the core of WTForms.

But unfortunately the WTForms team is interested on removing the extensions, and after recommendation from @crast I created the module wtforms-tornado which can be located on PyPI and GitHub. All suggestions are welcome!

Standard
Python

Bonzo: Minimalistic Python SMTP Proxy

In this week, I built Bonzo, a minismalistic SMTP Proxy built on top of Tornado.

The motivation was that in my current job, we need to do tests on email messages that are sent through our applications. It all started when I saw a GitHub repository that has the first component that we need: an SMTP server, I did a couple of tests and unfortunately I did not have good results, something that not convinced me was the inactivity of the project because it had more than 3 years without having maintenance –although it was not necessary because the protocol has not changed.

Then, a co-worker recommended me to review the smtpd module, and since I’m very stubborn in wanting to use Tornado for all… the next night I decided not to sleep until I have done an SMTP server with all that smtpd implemented based on the code repository that I found at the beginning.

The result was basically an implementation of smtpd to extend the Tornado’s TCPServer class. An example of how to use Bonzo is, as follow:

TODO:

  • Authentication: Probably I will provide a method hook for verify the athentication and to derivate the responsability to a implementor’s logic.
  • TSL: I should to implement a way to support the STARTTLS command.
  • Unit tests!

Links:

P.S.

About the name… I have selected “Bonzo” because it’s the nickname of the drummer of my favorite Rock band and the best world’s drummer.

Standard